About the data

We will analyze the “IBM HR Analytics Employee Attrition & Performance” dataset ((link)[https://www.kaggle.com/pavansubhasht/ibm-hr-analytics-attrition-dataset]). The dataset documentation says that it was synthesized by data scientists at IBM. Observations describe hypothetical employees. Rows measure the employee retention status, metrics about the workplace, and details about the employee.

Preparation

Libraries

suppressPackageStartupMessages(library(tidyverse))
suppressPackageStartupMessages(library(leaps))     # model selection tools
suppressPackageStartupMessages(library(faraway))   # VIF function
suppressPackageStartupMessages(library(MASS))       # Box-Cox
source('./lib/fmrhs.R') # requires tidyverse
source('./lib/Pretty correlation.R')

Loading the data

We load the data using a custom column mapping. A few considerations apply:

  • Several columns are originally numeric levels. We recode these from the data description.
  • A few columns are irrelevant or constant across the data. We drop these.
# ordered factor
ordered_factor = col_factor(levels = 1:5, ordered = F)

# column mappings
types = cols(
  # continuous variables
  Age = col_double(),              DailyRate = col_double(),  
  DistanceFromHome = col_double(), HourlyRate = col_double(),       
  #JobLevel = col_double(),
  JobLevel = col_factor(),
  # ordered factors we later recode
  #   e.g., JobSatisfaction ranges from 1 - 4 (= 'Low' to 'Very High')
  Education                = col_factor(levels = 1:5, ordered = F),
  EnvironmentSatisfaction  = col_factor(1:4, ordered = F),
  JobInvolvement           = col_factor(1:4, ordered = F),
  JobSatisfaction          = col_factor(levels = 1:4, ordered = F),
  PerformanceRating        = col_factor(levels = 1:4, ordered = F),
  RelationshipSatisfaction = col_factor(levels = 1:4, ordered = F),
  WorkLifeBalance          = col_factor(levels = 1:4, ordered = F),
  
  # other factors
  BusinessTravel =  col_factor(levels = c('Non-Travel', 'Travel_Rarely', 'Travel_Frequently'), 
                               ordered = F),
  Gender = col_factor(),            JobRole = col_factor(),
  MaritalStatus = col_factor(),     EducationField = col_factor(),
  
  # things we have decided to treat as factors
  Department = col_factor(),        # we presume we could map to a taxonomy
  StockOptionLevel = col_factor(),  # unclear scale
  
  # true/false
  Attrition = col_factor(),
  OverTime = col_factor(),
  
  # drop
  EmployeeCount = col_skip(),  # always 1
  StandardHours = col_skip(),  # always 80
  EmployeeNumber = col_skip(), # 1, 2, ...
  Over18 = col_skip(),         # always true
  
  # continous values
  MonthlyIncome = col_double(),       MonthlyRate = col_double(),              
  NumCompaniesWorked = col_double(),  PercentSalaryHike = col_double(),
  TotalWorkingYears = col_double(),   TrainingTimesLastYear = col_double(),
  WorkLifeBalance = col_double(),     YearsAtCompany = col_double(),           
  YearsInCurrentRole = col_double(),  YearsSinceLastPromotion = col_double(),
  YearsWithCurrManager = col_double()
)

# read in using mapping
ibm = read_csv("data/ibm.csv", col_types = types)

# rename levels
ibm$Education = recode(ibm$Education, 
                       '1' = 'Below College', '2' = 'College', '3' = 'Bachelor', '4' = 'Master', '5' = 'Doctor')
ibm$EnvironmentSatisfaction = recode(ibm$EnvironmentSatisfaction, 
                       '1' = 'Low', '2' = 'Medium', '3' = 'High', '4' = 'Very High')
ibm$JobInvolvement = recode(ibm$JobInvolvement, 
                       '1' = 'Low', '2' = 'Medium', '3' = 'High', '4' = 'Very High')
ibm$JobSatisfaction = recode(ibm$JobSatisfaction, 
                       '1' = 'Low', '2' = 'Medium', '3' = 'High', '4' = 'Very High')
ibm$PerformanceRating = recode(ibm$PerformanceRating, 
                       '1' = 'Low', '2' = 'Good', '3' = 'Excellent', '4' = 'Outstanding')
ibm$RelationshipSatisfaction = recode(ibm$RelationshipSatisfaction, 
                       '1' = 'Low', '2' = 'Medium', '3' = 'High', '4' = 'Very High')
ibm$WorkLifeBalance = recode(ibm$WorkLifeBalance, 
                       '1' = 'Bad', '2' = 'Good', '3' = 'Better', '4' = 'Best')

# set contrasts for yes/no levels
ibm$Attrition = relevel(ibm$Attrition, ref = "No")
ibm$OverTime = relevel(ibm$OverTime, ref = "No")

Additional data manipulations

We produce a defactored version of the data for which we have replaced factors with numeric vectors.

ibm.defactored = mutate_if(ibm, is.factor, ~ as.numeric(.x))

Sample observations

We show some sample data:

# a sample observation
t(head(ibm, n = 1))
                         [,1]             
Age                      "41"             
Attrition                "Yes"            
BusinessTravel           "Travel_Rarely"  
DailyRate                "1102"           
Department               "Sales"          
DistanceFromHome         "1"              
Education                "College"        
EducationField           "Life Sciences"  
EnvironmentSatisfaction  "Medium"         
Gender                   "Female"         
HourlyRate               "94"             
JobInvolvement           "High"           
JobLevel                 "2"              
JobRole                  "Sales Executive"
JobSatisfaction          "Very High"      
MaritalStatus            "Single"         
MonthlyIncome            "5993"           
MonthlyRate              "19479"          
NumCompaniesWorked       "8"              
OverTime                 "Yes"            
PercentSalaryHike        "11"             
PerformanceRating        "Excellent"      
RelationshipSatisfaction "Low"            
StockOptionLevel         "0"              
TotalWorkingYears        "8"              
TrainingTimesLastYear    "0"              
WorkLifeBalance          "Bad"            
YearsAtCompany           "6"              
YearsInCurrentRole       "4"              
YearsSinceLastPromotion  "0"              
YearsWithCurrManager     "5"              
head(ibm, n = 10)[,c(1:5)]

General Observations

There are 1,470 rows and 31 columns.

par(mfrow=c(2,2))
plot(ibm$EducationField)
plot(ibm$JobInvolvement)
plot(ibm$Education)
with(ibm, hist(Age))

summary(ibm)
      Age        Attrition            BusinessTravel   DailyRate                       Department  DistanceFromHome         Education  
 Min.   :18.00   No :1233   Non-Travel       : 150   Min.   : 102.0   Sales                 :446   Min.   : 1.000   Below College:170  
 1st Qu.:30.00   Yes: 237   Travel_Rarely    :1043   1st Qu.: 465.0   Research & Development:961   1st Qu.: 2.000   College      :282  
 Median :36.00              Travel_Frequently: 277   Median : 802.0   Human Resources       : 63   Median : 7.000   Bachelor     :572  
 Mean   :36.92                                       Mean   : 802.5                                Mean   : 9.193   Master       :398  
 3rd Qu.:43.00                                       3rd Qu.:1157.0                                3rd Qu.:14.000   Doctor       : 48  
 Max.   :60.00                                       Max.   :1499.0                                Max.   :29.000                      
                                                                                                                                       
          EducationField EnvironmentSatisfaction    Gender      HourlyRate       JobInvolvement JobLevel
 Life Sciences   :606    Low      :284           Female:588   Min.   : 30.00   Low      : 83    2:534   
 Other           : 82    Medium   :287           Male  :882   1st Qu.: 48.00   Medium   :375    1:543   
 Medical         :464    High     :453                        Median : 66.00   High     :868    3:218   
 Marketing       :159    Very High:446                        Mean   : 65.89   Very High:144    4:106   
 Technical Degree:132                                         3rd Qu.: 83.75                    5: 69   
 Human Resources : 27                                         Max.   :100.00                            
                                                                                                        
                      JobRole     JobSatisfaction  MaritalStatus MonthlyIncome    MonthlyRate    NumCompaniesWorked OverTime  
 Sales Executive          :326   Low      :289    Single  :470   Min.   : 1009   Min.   : 2094   Min.   :0.000      No :1054  
 Research Scientist       :292   Medium   :280    Married :673   1st Qu.: 2911   1st Qu.: 8047   1st Qu.:1.000      Yes: 416  
 Laboratory Technician    :259   High     :442    Divorced:327   Median : 4919   Median :14236   Median :2.000                
 Manufacturing Director   :145   Very High:459                   Mean   : 6503   Mean   :14313   Mean   :2.693                
 Healthcare Representative:131                                   3rd Qu.: 8379   3rd Qu.:20462   3rd Qu.:4.000                
 Manager                  :102                                   Max.   :19999   Max.   :26999   Max.   :9.000                
 (Other)                  :215                                                                                                
 PercentSalaryHike   PerformanceRating RelationshipSatisfaction StockOptionLevel TotalWorkingYears TrainingTimesLastYear
 Min.   :11.00     Low        :   0    Low      :276            0:631            Min.   : 0.00     Min.   :0.000        
 1st Qu.:12.00     Good       :   0    Medium   :303            1:596            1st Qu.: 6.00     1st Qu.:2.000        
 Median :14.00     Excellent  :1244    High     :459            3: 85            Median :10.00     Median :3.000        
 Mean   :15.21     Outstanding: 226    Very High:432            2:158            Mean   :11.28     Mean   :2.799        
 3rd Qu.:18.00                                                                   3rd Qu.:15.00     3rd Qu.:3.000        
 Max.   :25.00                                                                   Max.   :40.00     Max.   :6.000        
                                                                                                                        
 WorkLifeBalance YearsAtCompany   YearsInCurrentRole YearsSinceLastPromotion YearsWithCurrManager
 Bad   : 80      Min.   : 0.000   Min.   : 0.000     Min.   : 0.000          Min.   : 0.000      
 Good  :344      1st Qu.: 3.000   1st Qu.: 2.000     1st Qu.: 0.000          1st Qu.: 2.000      
 Better:893      Median : 5.000   Median : 3.000     Median : 1.000          Median : 3.000      
 Best  :153      Mean   : 7.008   Mean   : 4.229     Mean   : 2.188          Mean   : 4.123      
                 3rd Qu.: 9.000   3rd Qu.: 7.000     3rd Qu.: 3.000          3rd Qu.: 7.000      
                 Max.   :40.000   Max.   :18.000     Max.   :15.000          Max.   :17.000      
                                                                                                 

Correlation

We look at columns for which the correlation is greater than 70 in the defactored data:

p = round(cor(ibm.defactored) * 100)
for(i in 1:nrow(p)) {
  for(j in i:ncol(p)) {
    if(i == j) next
    val = p[i,j]
    if(abs(val) > 70) {
      r = row.names(p)[i]
      c = colnames(p)[j]
      str = paste("cor(", r, ", ", c, ") = ", val, "\n")
      cat(str)
    }
  }
}
cor( JobLevel ,  MonthlyIncome ) =  76 
cor( MonthlyIncome ,  TotalWorkingYears ) =  77 
cor( PercentSalaryHike ,  PerformanceRating ) =  77 
cor( YearsAtCompany ,  YearsInCurrentRole ) =  76 
cor( YearsAtCompany ,  YearsWithCurrManager ) =  77 
cor( YearsInCurrentRole ,  YearsWithCurrManager ) =  71 

We also examine the VIFs of the defactored data for general interest. We perform a formal test when we investigate specific models.

model.defactored = lm(MonthlyIncome ~ ., data = ibm.defactored)
model.defactored.vifs = faraway::vif(model.defactored)

# Five highest VIFs with this "defactored" model on Monthly income
# 
model.defactored.vifs[order(model.defactored.vifs, decreasing = T)][1:5]
      YearsAtCompany    TotalWorkingYears YearsWithCurrManager   YearsInCurrentRole    PercentSalaryHike 
            4.626309             3.922090             2.831112             2.754956             2.532532 

Question 1

Our first question is

What factors correlate with attrition?

Work

Selecting an initial model

We choose to investigate this using the leaps framework to evaluate possible variables to include. Given the fairly small data set, we perform an exhaustive search using regsubsets from the leaps package.

# this took about six minutes on one computer tested
system.time({
  allreg = regsubsets(MonthlyIncome ~ ., ibm, nbest = 1, really.big = T)
})
2  linear dependencies found
Reordering variables and trying again:
   user  system elapsed 
  6.040   0.024   6.087 

We now print out the top results for several criteria.

pp_allreg(allreg)
Maximized: r2 with value 0.947986365994346
   Formula: (response) ~  1 + JobLevel1 + JobLevel3 + JobLevel4 + JobLevel5 + JobRoleResearch Scientist + JobRoleLaboratory Technician + JobRoleManager + JobRoleSales Representative + JobRoleResearch Director

Maximized: adjr2 with value 0.9476657340039
   Formula: (response) ~  1 + JobLevel1 + JobLevel3 + JobLevel4 + JobLevel5 + JobRoleResearch Scientist + JobRoleLaboratory Technician + JobRoleManager + JobRoleSales Representative + JobRoleResearch Director

Minimized: mse with value 1159981.52574639
   Formula: (response) ~  1 + JobLevel1 + JobLevel3 + JobLevel4 + JobLevel5 + JobRoleResearch Scientist + JobRoleLaboratory Technician + JobRoleManager + JobRoleSales Representative + JobRoleResearch Director

Minimized: cp with value 66.1106375795891
   Formula: (response) ~  1 + JobLevel1 + JobLevel3 + JobLevel4 + JobLevel5 + JobRoleResearch Scientist + JobRoleLaboratory Technician + JobRoleManager + JobRoleSales Representative + JobRoleResearch Director

Minimized: bic with value -4272.75644465781
   Formula: (response) ~  1 + JobLevel1 + JobLevel3 + JobLevel4 + JobLevel5 + JobRoleResearch Scientist + JobRoleLaboratory Technician + JobRoleManager + JobRoleSales Representative + JobRoleResearch Director

Initial model evaluation

Since the model with the extreme value for every criterion is the same in the exhautive search, we fit the specified model:

model.allreg = lm(MonthlyIncome ~  1 + JobLevel + JobRole, data = ibm)

CORRECT ME.

The equation would be \[\begin{array}{rcl} \text{Monthly_Income} &=& \beta_0 + \beta_1 \times \text{BusinessTravel} + \beta_2 \times \text{EnvironmentSatisfaction} \\ && + \beta_3 \times \text{JobInvolvement} + \beta_4 \times \text{JobRole} + \beta_5 \times \text{JobSatisfaction} \\ && + \beta_6 \times \text{OverTime} + \beta_7 \times \text{StockOptionLevel} + \beta_8 \times \text{TotalWorkingYears} \end{array}\]

Basic features

summary(model.allreg)

Call:
lm(formula = MonthlyIncome ~ 1 + JobLevel + JobRole, data = ibm)

Residuals:
    Min      1Q  Median      3Q     Max 
-2912.3  -662.0   -87.3   651.8  4253.4 

Coefficients:
                                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)                       5762.72      64.63  89.161  < 2e-16 ***
JobLevel1                        -1878.46     102.01 -18.415  < 2e-16 ***
JobLevel3                         3474.85      92.98  37.370  < 2e-16 ***
JobLevel4                         7439.74     154.88  48.037  < 2e-16 ***
JobLevel5                        10089.59     202.41  49.848  < 2e-16 ***
JobRoleResearch Scientist        -1029.30     120.58  -8.536  < 2e-16 ***
JobRoleLaboratory Technician     -1115.24     120.58  -9.249  < 2e-16 ***
JobRoleManufacturing Director      -59.07     107.22  -0.551    0.582    
JobRoleHealthcare Representative    87.79     111.27   0.789    0.430    
JobRoleManager                    3328.58     177.16  18.789  < 2e-16 ***
JobRoleSales Representative      -1416.68     162.62  -8.712  < 2e-16 ***
JobRoleResearch Director          3357.60     167.92  19.995  < 2e-16 ***
JobRoleHuman Resources            -735.81     172.87  -4.256 2.21e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1071 on 1457 degrees of freedom
Multiple R-squared:  0.9487,    Adjusted R-squared:  0.9483 
F-statistic:  2246 on 12 and 1457 DF,  p-value: < 2.2e-16
model.simple = lm(MonthlyIncome ~ JobLevel, data = ibm)
summary(model.simple)

Call:
lm(formula = MonthlyIncome ~ JobLevel, data = ibm)

Residuals:
    Min      1Q  Median      3Q     Max 
-4607.3  -713.2   -93.9   694.1  4495.7 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  5502.28      55.80   98.61   <2e-16 ***
JobLevel1   -2715.36      78.58  -34.56   <2e-16 ***
JobLevel3    4314.98     103.63   41.64   <2e-16 ***
JobLevel4   10001.51     137.10   72.95   <2e-16 ***
JobLevel5   13689.55     164.94   83.00   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1289 on 1465 degrees of freedom
Multiple R-squared:  0.9252,    Adjusted R-squared:  0.925 
F-statistic:  4530 on 4 and 1465 DF,  p-value: < 2.2e-16

Multicollinearity

We first check for multicollinearity:

faraway::vif(model.allreg)
                       JobLevel1                        JobLevel3                        JobLevel4                        JobLevel5 
                        3.108806                         1.400625                         2.058392                         2.350623 
       JobRoleResearch Scientist     JobRoleLaboratory Technician    JobRoleManufacturing Director JobRoleHealthcare Representative 
                        2.968278                         2.706742                         1.310810                         1.289090 
                  JobRoleManager      JobRoleSales Representative         JobRoleResearch Director           JobRoleHuman Resources 
                        2.599189                         1.806889                         1.861053                         1.307837 

Linear assumptions

plot(model.allreg, which = 1)

boxcox(model.allreg, lambda = seq(0.5, 1, by= 0.1))

model.allreg.transform = lm(MonthlyIncome^0.7 ~  JobLevel + JobRole, data = ibm)
summary(model.allreg.transform)

Call:
lm(formula = MonthlyIncome^0.7 ~ JobLevel + JobRole, data = ibm)

Residuals:
     Min       1Q   Median       3Q      Max 
-159.257  -35.323   -3.147   34.838  205.714 

Coefficients:
                                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)                       426.643      3.387 125.952  < 2e-16 ***
JobLevel1                        -111.099      5.346 -20.781  < 2e-16 ***
JobLevel3                         170.382      4.873  34.963  < 2e-16 ***
JobLevel4                         334.287      8.117  41.184  < 2e-16 ***
JobLevel5                         430.551     10.608  40.587  < 2e-16 ***
JobRoleResearch Scientist         -55.250      6.319  -8.743  < 2e-16 ***
JobRoleLaboratory Technician      -59.876      6.320  -9.475  < 2e-16 ***
JobRoleManufacturing Director      -3.300      5.619  -0.587    0.557    
JobRoleHealthcare Representative    4.492      5.832   0.770    0.441    
JobRoleManager                    137.502      9.285  14.810  < 2e-16 ***
JobRoleSales Representative       -79.963      8.523  -9.382  < 2e-16 ***
JobRoleResearch Director          140.291      8.801  15.941  < 2e-16 ***
JobRoleHuman Resources            -40.182      9.060  -4.435 9.89e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 56.11 on 1457 degrees of freedom
Multiple R-squared:  0.936, Adjusted R-squared:  0.9354 
F-statistic:  1775 on 12 and 1457 DF,  p-value: < 2.2e-16
plot(model.allreg.transform, which = 1)
qqline(model.allreg.transform$residuals, col="red")

boxcox(model.allreg.transform)


plot(model.simple, which = 1)

boxcox(model.simple, lambda = seq(0.2, .6, by= 0.1))



#simple.transform = ibm
#simple.transform$MonthlyIncome = simple.transform$MonthlyIncome^0.5
model.simple.transform = lm(sqrt(MonthlyIncome) ~  JobLevel, data = ibm)


plot(model.simple.transform, which = 1)

{qqnorm(model.simple.transform$residuals, col="red")
qqline(model.simple.transform$residuals, col="red")}

boxcox(model.simple.transform)

summary(model.allreg.transform)

Call:
lm(formula = MonthlyIncome^0.7 ~ JobLevel + JobRole, data = ibm)

Residuals:
     Min       1Q   Median       3Q      Max 
-159.257  -35.323   -3.147   34.838  205.714 

Coefficients:
                                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)                       426.643      3.387 125.952  < 2e-16 ***
JobLevel1                        -111.099      5.346 -20.781  < 2e-16 ***
JobLevel3                         170.382      4.873  34.963  < 2e-16 ***
JobLevel4                         334.287      8.117  41.184  < 2e-16 ***
JobLevel5                         430.551     10.608  40.587  < 2e-16 ***
JobRoleResearch Scientist         -55.250      6.319  -8.743  < 2e-16 ***
JobRoleLaboratory Technician      -59.876      6.320  -9.475  < 2e-16 ***
JobRoleManufacturing Director      -3.300      5.619  -0.587    0.557    
JobRoleHealthcare Representative    4.492      5.832   0.770    0.441    
JobRoleManager                    137.502      9.285  14.810  < 2e-16 ***
JobRoleSales Representative       -79.963      8.523  -9.382  < 2e-16 ***
JobRoleResearch Director          140.291      8.801  15.941  < 2e-16 ***
JobRoleHuman Resources            -40.182      9.060  -4.435 9.89e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 56.11 on 1457 degrees of freedom
Multiple R-squared:  0.936, Adjusted R-squared:  0.9354 
F-statistic:  1775 on 12 and 1457 DF,  p-value: < 2.2e-16
summary(model.simple.transform)

Call:
lm(formula = sqrt(MonthlyIncome) ~ JobLevel, data = ibm)

Residuals:
     Min       1Q   Median       3Q      Max 
-28.3972  -5.2171  -0.2344   5.0746  26.4043 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  73.5857     0.3536  208.13   <2e-16 ***
JobLevel1   -21.2677     0.4979  -42.71   <2e-16 ***
JobLevel3    25.0771     0.6567   38.19   <2e-16 ***
JobLevel4    50.7090     0.8687   58.37   <2e-16 ***
JobLevel5    64.9366     1.0452   62.13   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 8.17 on 1465 degrees of freedom
Multiple R-squared:  0.9055,    Adjusted R-squared:  0.9052 
F-statistic:  3508 on 4 and 1465 DF,  p-value: < 2.2e-16

\[ E(\varepsilon) = 0, \quad \sigma^2 \text{ constant}\]

Stepwise

regnull = lm(MonthlyIncome ~  1, data = ibm)
regfull = lm(MonthlyIncome ~ ., data = ibm)

#step(regfull, scope=list(lower=regnull, upper=regfull), direction="backward")
#step(regfull, scope=list(lower=regnull, upper=regfull), direction="forward")
step(regfull, scope=list(lower=regnull, upper=regfull), direction="both", trace = 0)

Call:
lm(formula = MonthlyIncome ~ Gender + JobInvolvement + JobLevel + 
    JobRole + NumCompaniesWorked + StockOptionLevel + TotalWorkingYears + 
    YearsInCurrentRole, data = ibm)

Coefficients:
                     (Intercept)                        GenderMale              JobInvolvementMedium                JobInvolvementHigh  
                        5515.998                            80.608                          -199.391                          -316.879  
         JobInvolvementVery High                         JobLevel1                         JobLevel3                         JobLevel4  
                        -291.419                         -1594.622                          3254.474                          6838.395  
                       JobLevel5         JobRoleResearch Scientist      JobRoleLaboratory Technician     JobRoleManufacturing Director  
                        9405.617                         -1186.292                         -1286.324                           -87.914  
JobRoleHealthcare Representative                    JobRoleManager       JobRoleSales Representative          JobRoleResearch Director  
                           7.335                          3349.003                         -1474.261                          3389.699  
          JobRoleHuman Resources                NumCompaniesWorked                 StockOptionLevel1                 StockOptionLevel3  
                        -869.088                            29.323                           112.508                          -124.918  
               StockOptionLevel2                 TotalWorkingYears                YearsInCurrentRole  
                         -49.980                            33.183                            13.723  
model.best = lm(MonthlyIncome ~ Gender + JobInvolvement + JobLevel + JobRole + 
    NumCompaniesWorked + StockOptionLevel + TotalWorkingYears + 
    YearsInCurrentRole, data = ibm)

plot(model.best, which = 1)

boxcox(model.best, lambda = seq(0.3, 1.2, 0.1))


#best.transform  = ibm
#best.transform$MonthlyIncome = best.transform$MonthlyIncome^0.7
model.best.transform = lm(MonthlyIncome^0.7 ~ Gender + JobInvolvement + JobLevel + JobRole + 
    NumCompaniesWorked + StockOptionLevel + TotalWorkingYears + 
    YearsInCurrentRole, data = ibm )

plot(model.best.transform , which = 1)

boxcox(model.best.transform, lambda = seq(0.3, 1.2, 0.1))

Transformation of non factor predictors for all 3 models (allreg, simple, best)

transformDoubles = function(df, func) {
  dbls = sapply(df, is.double)
  copy = df
  copy[,dbls] = func(df[,dbls])
  copy
}
allregMonthlyIncome = ibm$MonthlyIncome^.7
allreg.transform.predictors = ibm[-c(17)]

## We tried 3 types of transformation to all of non factor predictors (ln, )
allreg.transform.factor = allreg.transform.predictors[,sapply(allreg.transform.predictors, is.factor)]
allreg.transform.double = allreg.transform.predictors[,sapply(allreg.transform.predictors, is.double)]
#allreg.transform.double = lapply(allreg.transform.double, function(x) 1/x)
#allreg.transform.double = exp(allreg.transform.double + 0.01)
allreg.transform.double = log(allreg.transform.double + 0.01)
allreg.transform.predictors = cbind(allreg.transform.factor, allreg.transform.double)
allreg.transform.predictors$MonthlyIncome = allregMonthlyIncome
# check function
res = transformDoubles(allreg.transform.predictors, function(x) { log(x + 0.01) })
NaNs producedNaNs producedNaNs producedNaNs producedNaNs producedNaNs producedNaNs produced
res$MonthlyIncome = ibm$MonthlyIncome^.7 # retain original
#dbls = sapply(allreg.transform.predictors, is.double)
#res[,names(dbls[dbls])] == allreg.transform.predictors[,names(dbls[dbls])]
#res[,names(allreg.transform.predictors)] == allreg.transform.predictors[,names(allreg.transform.predictors)]
model.allreg.transform.predictors = lm(MonthlyIncome ~  JobLevel + JobRole, data = allreg.transform.predictors)
plot(model.allreg.transform.predictors , which = 1)

boxcox(model.allreg.transform.predictors, lambda = seq(0.3, 1.2, 0.1))

simpleMonthlyIncome = ibm$MonthlyIncome^.5
simple.transform.predictors = ibm[-c(17)]

## We tried 3 types of transformation to all of non factor predictors (ln, )
simple.transform.factor = simple.transform.predictors[,sapply(simple.transform.predictors, is.factor)]
simple.transform.double = simple.transform.predictors[,sapply(simple.transform.predictors, is.double)]
#simple.transform.double = lapply(simple.transform.double, function(x) 1/x)
#simple.transform.double = exp(simple.transform.double + 0.01)
simple.transform.double = log(simple.transform.double + 0.01)
simple.transform.predictors = cbind(simple.transform.factor, simple.transform.double)
simple.transform.predictors$MonthlyIncome = simpleMonthlyIncome

model.simple.transform.predictors = lm(MonthlyIncome ~  JobLevel, data = simple.transform.predictors)
plot(model.simple.transform.predictors , which = 1)

boxcox(model.simple.transform.predictors, lambda = seq(0.3, 1.2, 0.1))

bestMontlyIncome = ibm$MonthlyIncome^.7
best.transform.predictors = ibm[-c(17)]

## We tried 3 types of transformation to all of non factor predictors 
best.transform.factor = best.transform.predictors[,sapply(best.transform.predictors, is.factor)]
best.transform.double = best.transform.predictors[,sapply(best.transform.predictors, is.double)]
#best.transform.double = lapply(best.transform.double, function(x) 1/x)
#best.transform.double = exp(best.transform.double + 0.01)
best.transform.double = log(best.transform.double + 0.01)
best.transform.predictors = cbind(best.transform.factor, best.transform.double)
best.transform.predictors$MonthlyIncome = bestMontlyIncome

model.best.transform.predictors = lm(MonthlyIncome ~ Gender + JobInvolvement + JobLevel + 
    JobRole + NumCompaniesWorked + StockOptionLevel + TotalWorkingYears + 
    YearsInCurrentRole, data = best.transform.predictors)
plot(model.best.transform.predictors , which = 1)

boxcox(model.best.transform.predictors, lambda = seq(0.3, 1.2, 0.1))


# Transform Y since boxcox does not contain 1
best.transform.predictors$MonthlyIncome = best.transform.predictors$MonthlyIncome^.8
model.best.transform.predictors = lm(MonthlyIncome ~ Gender + JobInvolvement + JobLevel + 
    JobRole + NumCompaniesWorked + StockOptionLevel + TotalWorkingYears + 
    YearsInCurrentRole, data = best.transform.predictors)
plot(model.best.transform.predictors , which = 1)

boxcox(model.best.transform.predictors, lambda = seq(0.3, 1.2, 0.1))

summary(model.best.transform.predictors)

Call:
lm(formula = MonthlyIncome ~ Gender + JobInvolvement + JobLevel + 
    JobRole + NumCompaniesWorked + StockOptionLevel + TotalWorkingYears + 
    YearsInCurrentRole, data = best.transform.predictors)

Residuals:
    Min      1Q  Median      3Q     Max 
-43.928  -8.484  -0.294   8.124  49.795 

Coefficients:
                                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)                      119.7846     1.9883  60.246  < 2e-16 ***
GenderMale                         0.6936     0.7032   0.986   0.3241    
JobInvolvementMedium              -1.9870     1.5947  -1.246   0.2130    
JobInvolvementHigh                -3.5640     1.5109  -2.359   0.0185 *  
JobInvolvementVery High           -2.9269     1.8135  -1.614   0.1068    
JobLevel1                        -23.2972     1.3379 -17.413  < 2e-16 ***
JobLevel3                         37.2868     1.1579  32.203  < 2e-16 ***
JobLevel4                         69.5899     1.9555  35.587  < 2e-16 ***
JobLevel5                         88.3766     2.5359  34.850  < 2e-16 ***
JobRoleResearch Scientist        -15.2874     1.4899 -10.260  < 2e-16 ***
JobRoleLaboratory Technician     -16.2473     1.4884 -10.916  < 2e-16 ***
JobRoleManufacturing Director     -1.0880     1.3133  -0.828   0.4076    
JobRoleHealthcare Representative   0.2405     1.3669   0.176   0.8604    
JobRoleManager                    29.5165     2.1727  13.585  < 2e-16 ***
JobRoleSales Representative      -18.3760     1.9989  -9.193  < 2e-16 ***
JobRoleResearch Director          30.4136     2.0584  14.776  < 2e-16 ***
JobRoleHuman Resources           -11.5227     2.1210  -5.433  6.5e-08 ***
NumCompaniesWorked                 0.1800     0.1782   1.010   0.3127    
StockOptionLevel1                  0.9284     0.7553   1.229   0.2192    
StockOptionLevel3                 -1.2227     1.5194  -0.805   0.4211    
StockOptionLevel2                 -1.0964     1.1779  -0.931   0.3521    
TotalWorkingYears                  4.2845     0.4933   8.685  < 2e-16 ***
YearsInCurrentRole                 0.3352     0.1681   1.994   0.0464 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 13.09 on 1447 degrees of freedom
Multiple R-squared:  0.9342,    Adjusted R-squared:  0.9332 
F-statistic: 933.7 on 22 and 1447 DF,  p-value: < 2.2e-16

Outliers Analysis

##residuals
res = model.best.transform.predictors$residuals 
##studentized residuals
student.res = rstandard(model.best.transform.predictors) 
##externally studentized residuals
ext.student.res = rstudent(model.best.transform.predictors) 

par(mfrow = c(1,3))
plot(model.best.transform.predictors$fitted.values,res,main="Residuals")
plot(model.best.transform.predictors$fitted.values,student.res,main="Studentized Residuals")
plot(model.best.transform.predictors$fitted.values,ext.student.res,main="Externally  Studentized Residuals")


n = length(best.transform.predictors$MonthlyIncome)
p = length(coef(model.best.transform.predictors))

##critical value using Bonferroni procedure
ext.student.res[abs(ext.student.res)>qt(1-0.05/(2*n), n-p-1)]
named numeric(0)
# Leverage point
lev<-lm.influence(model.best.transform.predictors)$hat 
length(lev[lev>2*p/n])
[1] 59
ibm[which(lev>2*p/n),]
plot(ibm$PerformanceRating)

DFFITS<-dffits(model.best.transform.predictors)
DFFITS[abs(DFFITS)>2*sqrt(p/n)]
        34         51         62         86         89        100        123        140        153        154        189        213 
-0.5164222 -0.3544993 -0.2753969 -0.3424822 -0.2668946 -0.3529784  0.3270479  0.2518419 -0.4118730 -0.3248535  0.3526946  0.2982517 
       254        263        312        336        344        391        425        441        458        472        487        511 
 0.2615659 -0.3398483 -0.3393454  0.2530077  0.2655421 -0.2555796 -0.2618717  0.3540281  0.2544505  0.4428963  0.3513636  0.3346399 
       512        523        530        552        557        565        630        758        783        787        790        871 
 0.2767958  0.2748183  0.2541577  0.2508164 -0.2907635  0.4020855  0.3006576  0.4161342  0.4504144  0.3548528  0.2621831  0.3557121 
       912        926        945        991       1013       1022       1036       1042       1064       1083       1087       1091 
-0.3326712 -0.4204300  0.4038659  0.2901220 -0.2518345  0.2550441 -0.2617855  0.2510447  0.2700827 -0.3135081 -0.3254631  0.4748637 
      1107       1120       1126       1204       1212       1223       1244       1299       1316       1327       1366       1370 
 0.2800953  0.3410709  0.2993764 -0.2541930  0.4724197 -0.3679182  0.3478758  0.2852621  0.3070194  0.3166156 -0.2800686  0.3871367 
      1374       1395       1403       1443       1464       1466 
-0.4022173  0.4448477 -0.3596750  0.3170717  0.4402406 -0.3441110 
DFBETAS<-dfbetas(model.best.transform.predictors)
length(DFBETAS[abs(DFBETAS)>2/sqrt(n)])
[1] 1711
#length(DFBETAS)
nrow(ibm)
[1] 1470
length(model.best.transform.predictors$residuals)
[1] 1470
COOKS<-cooks.distance(model.best.transform.predictors)
COOKS[COOKS>qf(0.5,p,n-p)]
named numeric(0)

Model Diagnostics

We now check for predictive performance.

length(model.best.transform.predictors$coefficients)
[1] 23
length(ibm[,1])
[1] 1
model.simple.rstudent     = rstudent(model.simple) 
n = nrow(ibm)
p = length(coef(model.simple))
plot(model.simple.rstudent,main="Externally Studentized Residuals", ylim=c(-4,4))
abline(h=qt(1-0.05/(2*n), n-p-1), col="red")
abline(h=-qt(1-0.05/(2*n), n-p-1), col="red")

model.simple.rstudent[abs(model.simple.rstudent)>qt(1-0.05/(2*n), n-p-1)]
named numeric(0)
lev = lm.influence(model.simple)$hat 
influence = lev[lev>2*p/n]
{
  plot(lev, main="Leverages", ylim=c(-0.8,0.8))
  abline(h=2*p/n, col="red")
  identify(lev)
}
integer(0)

ibm[which(lev>2*p/n),]
ibm.nosimplelev = ibm[-which(lev>2*p/n),]
model.simple.nolev = lm(MonthlyIncome ~ JobLevel, data = ibm.nosimplelev)
plot(model.simple.nolev)

plot(model.simple)

# Create subsets for each job level
jobLevel1 = subset(ibm, JobLevel == 1)
jobLevel2 = subset(ibm, JobLevel == 2)
jobLevel3 = subset(ibm, JobLevel == 3)
jobLevel4 = subset(ibm, JobLevel == 4)
jobLevel5 = subset(ibm, JobLevel == 5)
# using exhaustive search for job level 1
allreg.jobLevel1 <- regsubsets(MonthlyIncome ~., data=jobLevel1, nbest=1, really.big = T)
pp_allreg(allreg.jobLevel1)
Maximized: r2 with value 0.132988503672628
   Formula: (response) ~  1 + AttritionYes + JobRoleSales Representative + NumCompaniesWorked + PercentSalaryHike + PerformanceRatingOutstanding + TotalWorkingYears + TrainingTimesLastYear + YearsInCurrentRole + YearsSinceLastPromotion

Maximized: adjr2 with value 0.11834853469149
   Formula: (response) ~  1 + AttritionYes + JobRoleSales Representative + NumCompaniesWorked + PercentSalaryHike + PerformanceRatingOutstanding + TotalWorkingYears + TrainingTimesLastYear + YearsInCurrentRole + YearsSinceLastPromotion

Minimized: mse with value 180389.506969637
   Formula: (response) ~  1 + AttritionYes + JobRoleSales Representative + NumCompaniesWorked + PercentSalaryHike + PerformanceRatingOutstanding + TotalWorkingYears + TrainingTimesLastYear + YearsInCurrentRole + YearsSinceLastPromotion

Minimized: cp with value -6.67944653776021
   Formula: (response) ~  1 + AttritionYes + JobRoleSales Representative + NumCompaniesWorked + PercentSalaryHike + PerformanceRatingOutstanding + TotalWorkingYears + TrainingTimesLastYear + YearsInCurrentRole + YearsSinceLastPromotion

Minimized: bic with value -29.9370125416857
   Formula: (response) ~  1 + AttritionYes + TotalWorkingYears
# fit regression model for best r2 model for job level 1
model.jobLevel1 = lm(MonthlyIncome ~ 1 + Attrition + JobRole + NumCompaniesWorked + PercentSalaryHike + PerformanceRating + TotalWorkingYears + TrainingTimesLastYear + YearsInCurrentRole + YearsSinceLastPromotion, data=jobLevel1)

summary(model.jobLevel1)

Call:
lm(formula = MonthlyIncome ~ 1 + Attrition + JobRole + NumCompaniesWorked + 
    PercentSalaryHike + PerformanceRating + TotalWorkingYears + 
    TrainingTimesLastYear + YearsInCurrentRole + YearsSinceLastPromotion, 
    data = jobLevel1)

Residuals:
    Min      1Q  Median      3Q     Max 
-1589.8  -486.0  -106.1   391.9  2396.0 

Coefficients:
                             Estimate Std. Error t value Pr(>|t|)    
(Intercept)                   2333.25     215.12  10.846  < 2e-16 ***
AttritionYes                  -157.28      71.64  -2.196  0.02856 *  
JobRoleLaboratory Technician    60.41      68.75   0.879  0.37993    
JobRoleSales Representative   -178.83      96.38  -1.855  0.06408 .  
JobRoleHuman Resources         -67.64     131.42  -0.515  0.60698    
NumCompaniesWorked              26.03      14.46   1.800  0.07245 .  
PercentSalaryHike               24.84      13.22   1.879  0.06085 .  
PerformanceRatingOutstanding  -375.04     131.59  -2.850  0.00454 ** 
TotalWorkingYears               22.77      10.74   2.121  0.03442 *  
TrainingTimesLastYear          -45.56      23.54  -1.936  0.05345 .  
YearsInCurrentRole              62.07      17.96   3.457  0.00059 ***
YearsSinceLastPromotion        -31.91      19.08  -1.672  0.09507 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 703.4 on 531 degrees of freedom
Multiple R-squared:  0.1352,    Adjusted R-squared:  0.1172 
F-statistic: 7.544 on 11 and 531 DF,  p-value: 4.006e-12
# using exhaustive search for job level 2
allreg.jobLevel2 <- regsubsets(MonthlyIncome ~., data=jobLevel2, nbest=1, really.big = T)
# showing the outcome of exhaustive search for job level 2
pp_allreg(allreg.jobLevel2)
Maximized: r2 with value 0.150548565355962
   Formula: (response) ~  1 + BusinessTravelTravel_Rarely + DailyRate + EducationCollege + EducationFieldMedical + JobInvolvementHigh + JobRoleResearch Scientist + JobRoleLaboratory Technician + JobRoleSales Representative + TotalWorkingYears

Maximized: adjr2 with value 0.135958750638793
   Formula: (response) ~  1 + BusinessTravelTravel_Rarely + DailyRate + EducationCollege + EducationFieldMedical + JobInvolvementHigh + JobRoleResearch Scientist + JobRoleLaboratory Technician + JobRoleSales Representative + TotalWorkingYears

Minimized: mse with value 616551.587036018
   Formula: (response) ~  1 + BusinessTravelTravel_Rarely + DailyRate + EducationCollege + EducationFieldMedical + JobInvolvementHigh + JobRoleResearch Scientist + JobRoleLaboratory Technician + JobRoleSales Representative + TotalWorkingYears

Minimized: cp with value -18.5830550663767
   Formula: (response) ~  1 + EducationCollege + JobInvolvementHigh + JobRoleResearch Scientist + JobRoleLaboratory Technician + JobRoleSales Representative + TotalWorkingYears

Minimized: bic with value -47.0549705141564
   Formula: (response) ~  1 + JobRoleResearch Scientist + JobRoleLaboratory Technician + JobRoleSales Representative
# fit regression model for best r2 model for job level 2
model.jobLevel2 = lm(MonthlyIncome ~ 1 + BusinessTravel + DailyRate + Education + EducationField + JobInvolvement + JobRole + TotalWorkingYears, data=jobLevel2)

summary(model.jobLevel2)

Call:
lm(formula = MonthlyIncome ~ 1 + BusinessTravel + DailyRate + 
    Education + EducationField + JobInvolvement + JobRole + TotalWorkingYears, 
    data = jobLevel2)

Residuals:
    Min      1Q  Median      3Q     Max 
-2886.9  -925.3  -186.0   701.9  4761.7 

Coefficients:
                                   Estimate Std. Error t value Pr(>|t|)    
(Intercept)                       5843.8689   377.9581  15.462  < 2e-16 ***
BusinessTravelTravel_Rarely        268.5125   178.6849   1.503   0.1335    
BusinessTravelTravel_Frequently    107.7079   210.8828   0.511   0.6097    
DailyRate                            0.1985     0.1467   1.353   0.1767    
EducationCollege                  -202.1370   230.0177  -0.879   0.3799    
EducationBachelor                   -8.6939   220.5581  -0.039   0.9686    
EducationMaster                      4.3678   222.6972   0.020   0.9844    
EducationDoctor                     -1.5451   361.1265  -0.004   0.9966    
EducationFieldOther               -285.3864   245.9951  -1.160   0.2465    
EducationFieldMedical             -214.9235   141.4675  -1.519   0.1293    
EducationFieldMarketing            -42.1964   191.2709  -0.221   0.8255    
EducationFieldTechnical Degree     -74.0352   238.1091  -0.311   0.7560    
EducationFieldHuman Resources      641.1026   741.7347   0.864   0.3878    
JobInvolvementMedium              -412.6237   257.3944  -1.603   0.1095    
JobInvolvementHigh                -590.0371   239.0774  -2.468   0.0139 *  
JobInvolvementVery High           -473.0776   290.5380  -1.628   0.1041    
JobRoleResearch Scientist        -1037.4475   216.7746  -4.786 2.23e-06 ***
JobRoleLaboratory Technician     -1329.9945   212.1220  -6.270 7.69e-10 ***
JobRoleManufacturing Director     -153.1184   177.0351  -0.865   0.3875    
JobRoleHealthcare Representative    31.6401   187.5877   0.169   0.8661    
JobRoleSales Representative      -2112.1349   518.0567  -4.077 5.29e-05 ***
JobRoleHuman Resources            -696.2420   558.7380  -1.246   0.2133    
TotalWorkingYears                   23.2668    14.7949   1.573   0.1164    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1318 on 511 degrees of freedom
Multiple R-squared:  0.1629,    Adjusted R-squared:  0.1268 
F-statistic: 4.518 on 22 and 511 DF,  p-value: 1.244e-10
# using exhaustive search for job level 3
allreg.jobLevel3 <- regsubsets(MonthlyIncome ~., data=jobLevel3, nbest=1, really.big = T)
# showing the outcome of exhaustive search for job level 3
pp_allreg(allreg.jobLevel3)
Maximized: r2 with value 0.695564396196394
   Formula: (response) ~  1 + BusinessTravelTravel_Frequently + EducationMaster + JobRoleLaboratory Technician + JobRoleManager + JobRoleResearch Director + NumCompaniesWorked + RelationshipSatisfactionMedium + StockOptionLevel1 + TotalWorkingYears

Maximized: adjr2 with value 0.682391701801045
   Formula: (response) ~  1 + BusinessTravelTravel_Frequently + EducationMaster + JobRoleLaboratory Technician + JobRoleManager + JobRoleResearch Director + NumCompaniesWorked + RelationshipSatisfactionMedium + StockOptionLevel1 + TotalWorkingYears

Minimized: mse with value 147583.376854269
   Formula: (response) ~  1 + BusinessTravelTravel_Frequently + EducationMaster + JobRoleLaboratory Technician + JobRoleManager + JobRoleResearch Director + NumCompaniesWorked + RelationshipSatisfactionMedium + StockOptionLevel1 + TotalWorkingYears

Minimized: cp with value -8.9321030170415
   Formula: (response) ~  1 + BusinessTravelTravel_Frequently + EducationMaster + JobRoleLaboratory Technician + JobRoleManager + JobRoleResearch Director + NumCompaniesWorked + RelationshipSatisfactionMedium + StockOptionLevel1 + TotalWorkingYears

Minimized: bic with value -209.049030592466
   Formula: (response) ~  1 + JobRoleLaboratory Technician + JobRoleManager + JobRoleResearch Director + StockOptionLevel1 + TotalWorkingYears
plot(model.jobLevel3, which=1:2)
not plotting observations with leverage one:
  12

# using exhaustive search for job level 4
allreg.jobLevel4 <- regsubsets(MonthlyIncome ~., data=jobLevel4, nbest=1, really.big = T)
# showing the outcome of exhaustive search for job level 4
pp_allreg(allreg.jobLevel4)
Maximized: r2 with value 0.817682594713165
   Formula: (response) ~  1 + EducationDoctor + EnvironmentSatisfactionVery High + JobRoleManager + JobRoleResearch Director + JobSatisfactionVery High + MaritalStatusDivorced + MonthlyRate + PerformanceRatingExcellent + RelationshipSatisfactionVery High

Maximized: adjr2 with value 0.800590337967524
   Formula: (response) ~  1 + EducationDoctor + EnvironmentSatisfactionVery High + JobRoleManager + JobRoleResearch Director + JobSatisfactionVery High + MaritalStatusDivorced + MonthlyRate + PerformanceRatingExcellent + RelationshipSatisfactionVery High

Minimized: mse with value 43252.4357777102
   Formula: (response) ~  1 + EducationDoctor + EnvironmentSatisfactionVery High + JobRoleManager + JobRoleResearch Director + JobSatisfactionVery High + MaritalStatusDivorced + MonthlyRate + PerformanceRatingExcellent + RelationshipSatisfactionVery High

Minimized: cp with value -22.2039223919421
   Formula: (response) ~  1 + EducationDoctor + EnvironmentSatisfactionVery High + JobRoleManager + JobRoleResearch Director + JobSatisfactionVery High + MaritalStatusDivorced + MonthlyRate + PerformanceRatingExcellent + RelationshipSatisfactionVery High

Minimized: bic with value -135.961206585552
   Formula: (response) ~  1 + EnvironmentSatisfactionVery High + JobRoleManager + JobRoleResearch Director + JobSatisfactionVery High + MaritalStatusDivorced + MonthlyRate + RelationshipSatisfactionVery High
# fit regression model for best r2 model for job level 4
model.jobLevel4 = lm(MonthlyIncome ~ 1 + Education + EnvironmentSatisfaction + JobRole + JobSatisfaction + MaritalStatus + MonthlyRate + PerformanceRating + RelationshipSatisfaction, data=jobLevel4)

summary(model.jobLevel4)

Call:
lm(formula = MonthlyIncome ~ 1 + Education + EnvironmentSatisfaction + 
    JobRole + JobSatisfaction + MaritalStatus + MonthlyRate + 
    PerformanceRating + RelationshipSatisfaction, data = jobLevel4)

Residuals:
     Min       1Q   Median       3Q      Max 
-1976.37  -374.25    85.79   540.18  1531.90 

Coefficients:
                                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)                        1.416e+04  5.733e+02  24.701   <2e-16 ***
EducationCollege                  -4.505e+01  3.788e+02  -0.119   0.9056    
EducationBachelor                 -2.178e+02  3.416e+02  -0.638   0.5254    
EducationMaster                   -1.994e+02  3.623e+02  -0.550   0.5835    
EducationDoctor                   -7.818e+02  4.517e+02  -1.731   0.0872 .  
EnvironmentSatisfactionMedium     -1.898e+01  2.858e+02  -0.066   0.9472    
EnvironmentSatisfactionHigh       -1.026e+02  2.606e+02  -0.394   0.6947    
EnvironmentSatisfactionVery High   3.445e+02  2.579e+02   1.336   0.1852    
JobRoleManufacturing Director      3.054e+01  3.690e+02   0.083   0.9342    
JobRoleHealthcare Representative  -6.160e+01  3.823e+02  -0.161   0.8724    
JobRoleManager                     3.466e+03  2.752e+02  12.597   <2e-16 ***
JobRoleResearch Director           3.391e+03  2.931e+02  11.570   <2e-16 ***
JobSatisfactionMedium             -4.585e+02  2.753e+02  -1.666   0.0995 .  
JobSatisfactionHigh               -8.958e+01  2.648e+02  -0.338   0.7360    
JobSatisfactionVery High          -6.090e+02  2.468e+02  -2.467   0.0156 *  
MaritalStatusMarried              -1.245e+02  2.082e+02  -0.598   0.5515    
MaritalStatusDivorced             -6.697e+02  2.598e+02  -2.577   0.0117 *  
MonthlyRate                       -2.973e-02  1.255e-02  -2.369   0.0201 *  
PerformanceRatingOutstanding       4.377e+02  2.462e+02   1.777   0.0791 .  
RelationshipSatisfactionMedium    -4.456e+01  3.260e+02  -0.137   0.8916    
RelationshipSatisfactionHigh      -2.357e+02  3.004e+02  -0.785   0.4348    
RelationshipSatisfactionVery High  3.352e+02  3.066e+02   1.093   0.2774    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 837.9 on 84 degrees of freedom
Multiple R-squared:  0.8297,    Adjusted R-squared:  0.7871 
F-statistic: 19.49 on 21 and 84 DF,  p-value: < 2.2e-16
plot(model.jobLevel4, which=1:2)

# using exhaustive search for job level 5
allreg.jobLevel5 <- regsubsets(MonthlyIncome ~., data=jobLevel5, nbest=1, really.big = T)
# showing the outcome of exhaustive search for job level 5
pp_allreg(allreg.jobLevel5)
Maximized: r2 with value 0.368029993171166
   Formula: (response) ~  1 + BusinessTravelTravel_Frequently + DailyRate + EducationCollege + EducationFieldMarketing + EducationFieldTechnical Degree + EducationFieldHuman Resources + PercentSalaryHike + RelationshipSatisfactionHigh + StockOptionLevel1

Maximized: adjr2 with value 0.271627788739649
   Formula: (response) ~  1 + BusinessTravelTravel_Frequently + DailyRate + EducationCollege + EducationFieldMarketing + EducationFieldTechnical Degree + EducationFieldHuman Resources + PercentSalaryHike + RelationshipSatisfactionHigh + StockOptionLevel1

Minimized: mse with value 7727.55606339568
   Formula: (response) ~  1 + BusinessTravelTravel_Frequently + DailyRate + EducationCollege + EducationFieldMarketing + EducationFieldTechnical Degree + EducationFieldHuman Resources + PercentSalaryHike + RelationshipSatisfactionHigh + StockOptionLevel1

Minimized: cp with value -51.6575926901194
   Formula: (response) ~  1 + BusinessTravelTravel_Frequently

Minimized: bic with value 2.52520106593577
   Formula: (response) ~  1 + BusinessTravelTravel_Frequently + PercentSalaryHike
# fit regression model for best r2 model for job level 5
model.jobLevel5 = lm(MonthlyIncome ~ 1 + BusinessTravel + DailyRate + Education + EducationField + EducationField + PercentSalaryHike + RelationshipSatisfaction + StockOptionLevel, data=jobLevel5)

summary(model.jobLevel5)

Call:
lm(formula = MonthlyIncome ~ 1 + BusinessTravel + DailyRate + 
    Education + EducationField + EducationField + PercentSalaryHike + 
    RelationshipSatisfaction + StockOptionLevel, data = jobLevel5)

Residuals:
     Min       1Q   Median       3Q      Max 
-1104.33  -225.73    57.27   273.88   855.33 

Coefficients:
                                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)                       18870.2702   526.5751  35.836  < 2e-16 ***
BusinessTravelTravel_Rarely         243.1735   282.5654   0.861  0.39365    
BusinessTravelTravel_Frequently    -463.6854   306.1572  -1.515  0.13631    
DailyRate                            -0.3282     0.1463  -2.244  0.02942 *  
EducationCollege                   -516.9028   252.5670  -2.047  0.04608 *  
EducationBachelor                  -218.1748   236.6672  -0.922  0.36112    
EducationMaster                    -195.3336   246.9530  -0.791  0.43277    
EducationDoctor                    -570.6562   406.0389  -1.405  0.16620    
EducationFieldOther                 -55.1369   266.1451  -0.207  0.83674    
EducationFieldMedical              -114.0502   153.6924  -0.742  0.46159    
EducationFieldMarketing            -462.8369   212.9551  -2.173  0.03461 *  
EducationFieldTechnical Degree      366.7352   302.3985   1.213  0.23104    
EducationFieldHuman Resources      -518.6161   302.4229  -1.715  0.09269 .  
PercentSalaryHike                    53.9465    19.7568   2.731  0.00876 ** 
RelationshipSatisfactionMedium     -157.1884   205.1812  -0.766  0.44729    
RelationshipSatisfactionHigh       -258.4549   185.6177  -1.392  0.17009    
RelationshipSatisfactionVery High   -69.1480   177.3806  -0.390  0.69835    
StockOptionLevel1                   331.9605   141.4218   2.347  0.02299 *  
StockOptionLevel3                    76.5843   397.8251   0.193  0.84814    
StockOptionLevel2                    27.0371   285.4066   0.095  0.92491    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 460.1 on 49 degrees of freedom
Multiple R-squared:  0.419, Adjusted R-squared:  0.1937 
F-statistic:  1.86 on 19 and 49 DF,  p-value: 0.04169
plot(model.jobLevel5, which=1:2)

LS0tCnRpdGxlOiAiRW1wbG95ZWUgQXR0cml0aW9uIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKdXJsY29sb3I6IGJsdWUKYXV0aG9yOiBKb24gR29tZXogKGphZzJqKSwgTWljaGFlbCBMYW5nbWF5ciwgTmF0aGFuIEVuZ2xhbmQsIGFuZCBZaWhuZXcgRXNoZXR1Ci0tLQoKCiMgQWJvdXQgdGhlIGRhdGEKCldlIHdpbGwgYW5hbHl6ZSB0aGUgIklCTSBIUiBBbmFseXRpY3MgRW1wbG95ZWUgQXR0cml0aW9uICYgUGVyZm9ybWFuY2UiIGRhdGFzZXQgKChsaW5rKVtodHRwczovL3d3dy5rYWdnbGUuY29tL3BhdmFuc3ViaGFzaHQvaWJtLWhyLWFuYWx5dGljcy1hdHRyaXRpb24tZGF0YXNldF0pLiAgVGhlIGRhdGFzZXQgZG9jdW1lbnRhdGlvbiBzYXlzIHRoYXQgaXQgd2FzIHN5bnRoZXNpemVkIGJ5IGRhdGEgc2NpZW50aXN0cyBhdCBJQk0uICBPYnNlcnZhdGlvbnMgZGVzY3JpYmUgaHlwb3RoZXRpY2FsIGVtcGxveWVlcy4gIFJvd3MgbWVhc3VyZSB0aGUgZW1wbG95ZWUgcmV0ZW50aW9uIHN0YXR1cywgbWV0cmljcyBhYm91dCB0aGUgd29ya3BsYWNlLCBhbmQgZGV0YWlscyBhYm91dCB0aGUgZW1wbG95ZWUuCgojIFByZXBhcmF0aW9uCgojIyBMaWJyYXJpZXMKCmBgYHtyfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSh0aWR5dmVyc2UpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShsZWFwcykpICAgICAjIG1vZGVsIHNlbGVjdGlvbiB0b29scwpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShmYXJhd2F5KSkgICAjIFZJRiBmdW5jdGlvbgpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShNQVNTKSkgICAgICAgIyBCb3gtQ294CnNvdXJjZSgnLi9saWIvZm1yaHMuUicpICMgcmVxdWlyZXMgdGlkeXZlcnNlCnNvdXJjZSgnLi9saWIvUHJldHR5IGNvcnJlbGF0aW9uLlInKQpgYGAKCiMjIExvYWRpbmcgdGhlIGRhdGEKV2UgbG9hZCB0aGUgZGF0YSB1c2luZyBhIGN1c3RvbSBjb2x1bW4gbWFwcGluZy4gIEEgZmV3IGNvbnNpZGVyYXRpb25zIGFwcGx5OgoKKiBTZXZlcmFsIGNvbHVtbnMgYXJlIG9yaWdpbmFsbHkgbnVtZXJpYyBsZXZlbHMuICBXZSByZWNvZGUgdGhlc2UgZnJvbSB0aGUgZGF0YSBkZXNjcmlwdGlvbi4KKiBBIGZldyBjb2x1bW5zIGFyZSBpcnJlbGV2YW50IG9yIGNvbnN0YW50IGFjcm9zcyB0aGUgZGF0YS4gIFdlIGRyb3AgdGhlc2UuCgpgYGB7cn0KIyBvcmRlcmVkIGZhY3RvcgpvcmRlcmVkX2ZhY3RvciA9IGNvbF9mYWN0b3IobGV2ZWxzID0gMTo1LCBvcmRlcmVkID0gRikKCiMgY29sdW1uIG1hcHBpbmdzCnR5cGVzID0gY29scygKICAjIGNvbnRpbnVvdXMgdmFyaWFibGVzCiAgQWdlID0gY29sX2RvdWJsZSgpLCAgICAgICAgICAgICAgRGFpbHlSYXRlID0gY29sX2RvdWJsZSgpLCAgCiAgRGlzdGFuY2VGcm9tSG9tZSA9IGNvbF9kb3VibGUoKSwgSG91cmx5UmF0ZSA9IGNvbF9kb3VibGUoKSwgICAgICAgCiAgI0pvYkxldmVsID0gY29sX2RvdWJsZSgpLAogIEpvYkxldmVsID0gY29sX2ZhY3RvcigpLAogICMgb3JkZXJlZCBmYWN0b3JzIHdlIGxhdGVyIHJlY29kZQogICMgICBlLmcuLCBKb2JTYXRpc2ZhY3Rpb24gcmFuZ2VzIGZyb20gMSAtIDQgKD0gJ0xvdycgdG8gJ1ZlcnkgSGlnaCcpCiAgRWR1Y2F0aW9uICAgICAgICAgICAgICAgID0gY29sX2ZhY3RvcihsZXZlbHMgPSAxOjUsIG9yZGVyZWQgPSBGKSwKICBFbnZpcm9ubWVudFNhdGlzZmFjdGlvbiAgPSBjb2xfZmFjdG9yKDE6NCwgb3JkZXJlZCA9IEYpLAogIEpvYkludm9sdmVtZW50ICAgICAgICAgICA9IGNvbF9mYWN0b3IoMTo0LCBvcmRlcmVkID0gRiksCiAgSm9iU2F0aXNmYWN0aW9uICAgICAgICAgID0gY29sX2ZhY3RvcihsZXZlbHMgPSAxOjQsIG9yZGVyZWQgPSBGKSwKICBQZXJmb3JtYW5jZVJhdGluZyAgICAgICAgPSBjb2xfZmFjdG9yKGxldmVscyA9IDE6NCwgb3JkZXJlZCA9IEYpLAogIFJlbGF0aW9uc2hpcFNhdGlzZmFjdGlvbiA9IGNvbF9mYWN0b3IobGV2ZWxzID0gMTo0LCBvcmRlcmVkID0gRiksCiAgV29ya0xpZmVCYWxhbmNlICAgICAgICAgID0gY29sX2ZhY3RvcihsZXZlbHMgPSAxOjQsIG9yZGVyZWQgPSBGKSwKICAKICAjIG90aGVyIGZhY3RvcnMKICBCdXNpbmVzc1RyYXZlbCA9ICBjb2xfZmFjdG9yKGxldmVscyA9IGMoJ05vbi1UcmF2ZWwnLCAnVHJhdmVsX1JhcmVseScsICdUcmF2ZWxfRnJlcXVlbnRseScpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBGKSwKICBHZW5kZXIgPSBjb2xfZmFjdG9yKCksICAgICAgICAgICAgSm9iUm9sZSA9IGNvbF9mYWN0b3IoKSwKICBNYXJpdGFsU3RhdHVzID0gY29sX2ZhY3RvcigpLCAgICAgRWR1Y2F0aW9uRmllbGQgPSBjb2xfZmFjdG9yKCksCiAgCiAgIyB0aGluZ3Mgd2UgaGF2ZSBkZWNpZGVkIHRvIHRyZWF0IGFzIGZhY3RvcnMKICBEZXBhcnRtZW50ID0gY29sX2ZhY3RvcigpLCAgICAgICAgIyB3ZSBwcmVzdW1lIHdlIGNvdWxkIG1hcCB0byBhIHRheG9ub215CiAgU3RvY2tPcHRpb25MZXZlbCA9IGNvbF9mYWN0b3IoKSwgICMgdW5jbGVhciBzY2FsZQogIAogICMgdHJ1ZS9mYWxzZQogIEF0dHJpdGlvbiA9IGNvbF9mYWN0b3IoKSwKICBPdmVyVGltZSA9IGNvbF9mYWN0b3IoKSwKICAKICAjIGRyb3AKICBFbXBsb3llZUNvdW50ID0gY29sX3NraXAoKSwgICMgYWx3YXlzIDEKICBTdGFuZGFyZEhvdXJzID0gY29sX3NraXAoKSwgICMgYWx3YXlzIDgwCiAgRW1wbG95ZWVOdW1iZXIgPSBjb2xfc2tpcCgpLCAjIDEsIDIsIC4uLgogIE92ZXIxOCA9IGNvbF9za2lwKCksICAgICAgICAgIyBhbHdheXMgdHJ1ZQogIAogICMgY29udGlub3VzIHZhbHVlcwogIE1vbnRobHlJbmNvbWUgPSBjb2xfZG91YmxlKCksICAgICAgIE1vbnRobHlSYXRlID0gY29sX2RvdWJsZSgpLCAgICAgICAgICAgICAgCiAgTnVtQ29tcGFuaWVzV29ya2VkID0gY29sX2RvdWJsZSgpLCAgUGVyY2VudFNhbGFyeUhpa2UgPSBjb2xfZG91YmxlKCksCiAgVG90YWxXb3JraW5nWWVhcnMgPSBjb2xfZG91YmxlKCksICAgVHJhaW5pbmdUaW1lc0xhc3RZZWFyID0gY29sX2RvdWJsZSgpLAogIFdvcmtMaWZlQmFsYW5jZSA9IGNvbF9kb3VibGUoKSwgICAgIFllYXJzQXRDb21wYW55ID0gY29sX2RvdWJsZSgpLCAgICAgICAgICAgCiAgWWVhcnNJbkN1cnJlbnRSb2xlID0gY29sX2RvdWJsZSgpLCAgWWVhcnNTaW5jZUxhc3RQcm9tb3Rpb24gPSBjb2xfZG91YmxlKCksCiAgWWVhcnNXaXRoQ3Vyck1hbmFnZXIgPSBjb2xfZG91YmxlKCkKKQoKIyByZWFkIGluIHVzaW5nIG1hcHBpbmcKaWJtID0gcmVhZF9jc3YoImRhdGEvaWJtLmNzdiIsIGNvbF90eXBlcyA9IHR5cGVzKQoKIyByZW5hbWUgbGV2ZWxzCmlibSRFZHVjYXRpb24gPSByZWNvZGUoaWJtJEVkdWNhdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgJzEnID0gJ0JlbG93IENvbGxlZ2UnLCAnMicgPSAnQ29sbGVnZScsICczJyA9ICdCYWNoZWxvcicsICc0JyA9ICdNYXN0ZXInLCAnNScgPSAnRG9jdG9yJykKaWJtJEVudmlyb25tZW50U2F0aXNmYWN0aW9uID0gcmVjb2RlKGlibSRFbnZpcm9ubWVudFNhdGlzZmFjdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgJzEnID0gJ0xvdycsICcyJyA9ICdNZWRpdW0nLCAnMycgPSAnSGlnaCcsICc0JyA9ICdWZXJ5IEhpZ2gnKQppYm0kSm9iSW52b2x2ZW1lbnQgPSByZWNvZGUoaWJtJEpvYkludm9sdmVtZW50LCAKICAgICAgICAgICAgICAgICAgICAgICAnMScgPSAnTG93JywgJzInID0gJ01lZGl1bScsICczJyA9ICdIaWdoJywgJzQnID0gJ1ZlcnkgSGlnaCcpCmlibSRKb2JTYXRpc2ZhY3Rpb24gPSByZWNvZGUoaWJtJEpvYlNhdGlzZmFjdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgJzEnID0gJ0xvdycsICcyJyA9ICdNZWRpdW0nLCAnMycgPSAnSGlnaCcsICc0JyA9ICdWZXJ5IEhpZ2gnKQppYm0kUGVyZm9ybWFuY2VSYXRpbmcgPSByZWNvZGUoaWJtJFBlcmZvcm1hbmNlUmF0aW5nLCAKICAgICAgICAgICAgICAgICAgICAgICAnMScgPSAnTG93JywgJzInID0gJ0dvb2QnLCAnMycgPSAnRXhjZWxsZW50JywgJzQnID0gJ091dHN0YW5kaW5nJykKaWJtJFJlbGF0aW9uc2hpcFNhdGlzZmFjdGlvbiA9IHJlY29kZShpYm0kUmVsYXRpb25zaGlwU2F0aXNmYWN0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAnMScgPSAnTG93JywgJzInID0gJ01lZGl1bScsICczJyA9ICdIaWdoJywgJzQnID0gJ1ZlcnkgSGlnaCcpCmlibSRXb3JrTGlmZUJhbGFuY2UgPSByZWNvZGUoaWJtJFdvcmtMaWZlQmFsYW5jZSwgCiAgICAgICAgICAgICAgICAgICAgICAgJzEnID0gJ0JhZCcsICcyJyA9ICdHb29kJywgJzMnID0gJ0JldHRlcicsICc0JyA9ICdCZXN0JykKCiMgc2V0IGNvbnRyYXN0cyBmb3IgeWVzL25vIGxldmVscwppYm0kQXR0cml0aW9uID0gcmVsZXZlbChpYm0kQXR0cml0aW9uLCByZWYgPSAiTm8iKQppYm0kT3ZlclRpbWUgPSByZWxldmVsKGlibSRPdmVyVGltZSwgcmVmID0gIk5vIikKCmBgYAoKIyMgQWRkaXRpb25hbCBkYXRhIG1hbmlwdWxhdGlvbnMKCldlIHByb2R1Y2UgYSBkZWZhY3RvcmVkIHZlcnNpb24gb2YgdGhlIGRhdGEgZm9yIHdoaWNoIHdlIGhhdmUgcmVwbGFjZWQgZmFjdG9ycyB3aXRoIG51bWVyaWMgdmVjdG9ycy4KYGBge3J9CmlibS5kZWZhY3RvcmVkID0gbXV0YXRlX2lmKGlibSwgaXMuZmFjdG9yLCB+IGFzLm51bWVyaWMoLngpKQpgYGAKCiMjIFNhbXBsZSBvYnNlcnZhdGlvbnMKCldlIHNob3cgc29tZSBzYW1wbGUgZGF0YToKYGBge3J9CiMgYSBzYW1wbGUgb2JzZXJ2YXRpb24KdChoZWFkKGlibSwgbiA9IDEpKQpgYGAKCmBgYHtyfQpoZWFkKGlibSwgbiA9IDEwKVssYygxOjUpXQpgYGAKCiMgR2VuZXJhbCBPYnNlcnZhdGlvbnMKClRoZXJlIGFyZSAxLDQ3MCByb3dzIGFuZCAzMSBjb2x1bW5zLiAgCgpgYGB7cn0KCmBgYAoKCgoKYGBge3J9CnBhcihtZnJvdz1jKDIsMikpCnBsb3QoaWJtJEVkdWNhdGlvbkZpZWxkKQpwbG90KGlibSRKb2JJbnZvbHZlbWVudCkKcGxvdChpYm0kRWR1Y2F0aW9uKQp3aXRoKGlibSwgaGlzdChBZ2UpKQpgYGAKCmBgYHtyfQoKYGBgCgpgYGB7cn0Kc3VtbWFyeShpYm0pCmBgYAoKIyMgQ29ycmVsYXRpb24KCldlIGxvb2sgYXQgY29sdW1ucyBmb3Igd2hpY2ggdGhlIGNvcnJlbGF0aW9uIGlzIGdyZWF0ZXIgdGhhbiA3MCBpbiB0aGUgZGVmYWN0b3JlZCBkYXRhOgpgYGB7cn0KcCA9IHJvdW5kKGNvcihpYm0uZGVmYWN0b3JlZCkgKiAxMDApCmZvcihpIGluIDE6bnJvdyhwKSkgewogIGZvcihqIGluIGk6bmNvbChwKSkgewogICAgaWYoaSA9PSBqKSBuZXh0CiAgICB2YWwgPSBwW2ksal0KICAgIGlmKGFicyh2YWwpID4gNzApIHsKICAgICAgciA9IHJvdy5uYW1lcyhwKVtpXQogICAgICBjID0gY29sbmFtZXMocClbal0KICAgICAgc3RyID0gcGFzdGUoImNvcigiLCByLCAiLCAiLCBjLCAiKSA9ICIsIHZhbCwgIlxuIikKICAgICAgY2F0KHN0cikKICAgIH0KICB9Cn0KYGBgCgoKV2UgYWxzbyBleGFtaW5lIHRoZSBWSUZzIG9mIHRoZSBkZWZhY3RvcmVkIGRhdGEgZm9yIGdlbmVyYWwgaW50ZXJlc3QuICBXZSBwZXJmb3JtIGEgZm9ybWFsIHRlc3Qgd2hlbiB3ZSBpbnZlc3RpZ2F0ZSBzcGVjaWZpYyBtb2RlbHMuCmBgYHtyfQptb2RlbC5kZWZhY3RvcmVkID0gbG0oTW9udGhseUluY29tZSB+IC4sIGRhdGEgPSBpYm0uZGVmYWN0b3JlZCkKbW9kZWwuZGVmYWN0b3JlZC52aWZzID0gZmFyYXdheTo6dmlmKG1vZGVsLmRlZmFjdG9yZWQpCgojIEZpdmUgaGlnaGVzdCBWSUZzIHdpdGggdGhpcyAiZGVmYWN0b3JlZCIgbW9kZWwgb24gTW9udGhseSBpbmNvbWUKIyAKbW9kZWwuZGVmYWN0b3JlZC52aWZzW29yZGVyKG1vZGVsLmRlZmFjdG9yZWQudmlmcywgZGVjcmVhc2luZyA9IFQpXVsxOjVdCmBgYAoKCgojIFF1ZXN0aW9uIDEKCk91ciBmaXJzdCBxdWVzdGlvbiBpcwoKPiBXaGF0IGZhY3RvcnMgY29ycmVsYXRlIHdpdGggYXR0cml0aW9uPwoKIyMgV29yawoKIyMjIFNlbGVjdGluZyBhbiBpbml0aWFsIG1vZGVsCgpXZSBjaG9vc2UgdG8gaW52ZXN0aWdhdGUgdGhpcyB1c2luZyB0aGUgbGVhcHMgZnJhbWV3b3JrIHRvIGV2YWx1YXRlIHBvc3NpYmxlIHZhcmlhYmxlcyB0byBpbmNsdWRlLiAgR2l2ZW4gdGhlIGZhaXJseSBzbWFsbCBkYXRhIHNldCwgd2UgcGVyZm9ybSBhbiBleGhhdXN0aXZlIHNlYXJjaCB1c2luZyBgcmVnc3Vic2V0c2AgZnJvbSB0aGUgbGVhcHMgcGFja2FnZS4KCmBgYHtyfQojIHRoaXMgdG9vayBhYm91dCBzaXggbWludXRlcyBvbiBvbmUgY29tcHV0ZXIgdGVzdGVkCnN5c3RlbS50aW1lKHsKICBhbGxyZWcgPSByZWdzdWJzZXRzKE1vbnRobHlJbmNvbWUgfiAuLCBpYm0sIG5iZXN0ID0gMSwgcmVhbGx5LmJpZyA9IFQpCn0pCmBgYAoKV2Ugbm93IHByaW50IG91dCB0aGUgdG9wIHJlc3VsdHMgZm9yIHNldmVyYWwgY3JpdGVyaWEuCmBgYHtyfQpwcF9hbGxyZWcoYWxscmVnKQpgYGAKCiMjIyBJbml0aWFsIG1vZGVsIGV2YWx1YXRpb24KClNpbmNlIHRoZSBtb2RlbCB3aXRoIHRoZSBleHRyZW1lIHZhbHVlIGZvciBldmVyeSBjcml0ZXJpb24gaXMgdGhlIHNhbWUgaW4gdGhlIGV4aGF1dGl2ZSBzZWFyY2gsIHdlIGZpdCB0aGUgc3BlY2lmaWVkIG1vZGVsOgpgYGB7cn0KbW9kZWwuYWxscmVnID0gbG0oTW9udGhseUluY29tZSB+ICAxICsgSm9iTGV2ZWwgKyBKb2JSb2xlLCBkYXRhID0gaWJtKQpgYGAKCioqKkNPUlJFQ1QgTUUuKioqCgpUaGUgZXF1YXRpb24gd291bGQgYmUgCiQkXGJlZ2lue2FycmF5fXtyY2x9Clx0ZXh0e01vbnRobHlfSW5jb21lfSAmPSYgXGJldGFfMCArIFxiZXRhXzEgXHRpbWVzIFx0ZXh0e0J1c2luZXNzVHJhdmVsfSArIFxiZXRhXzIgXHRpbWVzIFx0ZXh0e0Vudmlyb25tZW50U2F0aXNmYWN0aW9ufSAKICAgICAgICAgICAgICAgICAgICBcXCAmJiArIFxiZXRhXzMgXHRpbWVzIFx0ZXh0e0pvYkludm9sdmVtZW50fSArIFxiZXRhXzQgXHRpbWVzIFx0ZXh0e0pvYlJvbGV9ICsgXGJldGFfNSBcdGltZXMgXHRleHR7Sm9iU2F0aXNmYWN0aW9ufQogICAgICAgICAgICAgICAgICAgIFxcICYmICsgXGJldGFfNiBcdGltZXMgXHRleHR7T3ZlclRpbWV9ICsgXGJldGFfNyBcdGltZXMgXHRleHR7U3RvY2tPcHRpb25MZXZlbH0gKyBcYmV0YV84IFx0aW1lcyBcdGV4dHtUb3RhbFdvcmtpbmdZZWFyc30KXGVuZHthcnJheX0kJAoKIyMjIyBCYXNpYyBmZWF0dXJlcwoKYGBge3J9CnN1bW1hcnkobW9kZWwuYWxscmVnKQpgYGAKCmBgYHtyfQptb2RlbC5zaW1wbGUgPSBsbShNb250aGx5SW5jb21lIH4gSm9iTGV2ZWwsIGRhdGEgPSBpYm0pCnN1bW1hcnkobW9kZWwuc2ltcGxlKQpgYGAKCgojIyMjIE11bHRpY29sbGluZWFyaXR5CgpXZSBmaXJzdCBjaGVjayBmb3IgbXVsdGljb2xsaW5lYXJpdHk6CgpgYGB7cn0KZmFyYXdheTo6dmlmKG1vZGVsLmFsbHJlZykKYGBgCgojIyMjIExpbmVhciBhc3N1bXB0aW9ucwpgYGB7cn0KcGxvdChtb2RlbC5hbGxyZWcsIHdoaWNoID0gMSkKYm94Y294KG1vZGVsLmFsbHJlZywgbGFtYmRhID0gc2VxKDAuNSwgMSwgYnk9IDAuMSkpCmBgYAoKCmBgYHtyfQptb2RlbC5hbGxyZWcudHJhbnNmb3JtID0gbG0oTW9udGhseUluY29tZV4wLjcgfiAgSm9iTGV2ZWwgKyBKb2JSb2xlLCBkYXRhID0gaWJtKQpzdW1tYXJ5KG1vZGVsLmFsbHJlZy50cmFuc2Zvcm0pCgpwbG90KG1vZGVsLmFsbHJlZy50cmFuc2Zvcm0sIHdoaWNoID0gMSkKcXFsaW5lKG1vZGVsLmFsbHJlZy50cmFuc2Zvcm0kcmVzaWR1YWxzLCBjb2w9InJlZCIpCmJveGNveChtb2RlbC5hbGxyZWcudHJhbnNmb3JtKQpgYGAKCmBgYHtyfQoKcGxvdChtb2RlbC5zaW1wbGUsIHdoaWNoID0gMSkKYm94Y294KG1vZGVsLnNpbXBsZSwgbGFtYmRhID0gc2VxKDAuMiwgLjYsIGJ5PSAwLjEpKQoKCiNzaW1wbGUudHJhbnNmb3JtID0gaWJtCiNzaW1wbGUudHJhbnNmb3JtJE1vbnRobHlJbmNvbWUgPSBzaW1wbGUudHJhbnNmb3JtJE1vbnRobHlJbmNvbWVeMC41Cm1vZGVsLnNpbXBsZS50cmFuc2Zvcm0gPSBsbShzcXJ0KE1vbnRobHlJbmNvbWUpIH4gIEpvYkxldmVsLCBkYXRhID0gaWJtKQoKCnBsb3QobW9kZWwuc2ltcGxlLnRyYW5zZm9ybSwgd2hpY2ggPSAxKQp7cXFub3JtKG1vZGVsLnNpbXBsZS50cmFuc2Zvcm0kcmVzaWR1YWxzLCBjb2w9InJlZCIpCnFxbGluZShtb2RlbC5zaW1wbGUudHJhbnNmb3JtJHJlc2lkdWFscywgY29sPSJyZWQiKX0KYm94Y294KG1vZGVsLnNpbXBsZS50cmFuc2Zvcm0pCgpgYGAKYGBge3J9CnN1bW1hcnkobW9kZWwuYWxscmVnLnRyYW5zZm9ybSkKc3VtbWFyeShtb2RlbC5zaW1wbGUudHJhbnNmb3JtKQpgYGAKCgoKJCQgRShcdmFyZXBzaWxvbikgPSAwLCBccXVhZCBcc2lnbWFeMiBcdGV4dHsgY29uc3RhbnR9JCQKCiMjIyMgU3RlcHdpc2UKYGBge3J9CnJlZ251bGwgPSBsbShNb250aGx5SW5jb21lIH4gIDEsIGRhdGEgPSBpYm0pCnJlZ2Z1bGwgPSBsbShNb250aGx5SW5jb21lIH4gLiwgZGF0YSA9IGlibSkKCiNzdGVwKHJlZ2Z1bGwsIHNjb3BlPWxpc3QobG93ZXI9cmVnbnVsbCwgdXBwZXI9cmVnZnVsbCksIGRpcmVjdGlvbj0iYmFja3dhcmQiKQojc3RlcChyZWdmdWxsLCBzY29wZT1saXN0KGxvd2VyPXJlZ251bGwsIHVwcGVyPXJlZ2Z1bGwpLCBkaXJlY3Rpb249ImZvcndhcmQiKQpzdGVwKHJlZ2Z1bGwsIHNjb3BlPWxpc3QobG93ZXI9cmVnbnVsbCwgdXBwZXI9cmVnZnVsbCksIGRpcmVjdGlvbj0iYm90aCIsIHRyYWNlID0gMCkKYGBgCmBgYHtyfQptb2RlbC5iZXN0ID0gbG0oTW9udGhseUluY29tZSB+IEdlbmRlciArIEpvYkludm9sdmVtZW50ICsgSm9iTGV2ZWwgKyBKb2JSb2xlICsgCiAgICBOdW1Db21wYW5pZXNXb3JrZWQgKyBTdG9ja09wdGlvbkxldmVsICsgVG90YWxXb3JraW5nWWVhcnMgKyAKICAgIFllYXJzSW5DdXJyZW50Um9sZSwgZGF0YSA9IGlibSkKCnBsb3QobW9kZWwuYmVzdCwgd2hpY2ggPSAxKQpib3hjb3gobW9kZWwuYmVzdCwgbGFtYmRhID0gc2VxKDAuMywgMS4yLCAwLjEpKQoKI2Jlc3QudHJhbnNmb3JtICA9IGlibQojYmVzdC50cmFuc2Zvcm0kTW9udGhseUluY29tZSA9IGJlc3QudHJhbnNmb3JtJE1vbnRobHlJbmNvbWVeMC43Cm1vZGVsLmJlc3QudHJhbnNmb3JtID0gbG0oTW9udGhseUluY29tZV4wLjcgfiBHZW5kZXIgKyBKb2JJbnZvbHZlbWVudCArIEpvYkxldmVsICsgSm9iUm9sZSArIAogICAgTnVtQ29tcGFuaWVzV29ya2VkICsgU3RvY2tPcHRpb25MZXZlbCArIFRvdGFsV29ya2luZ1llYXJzICsgCiAgICBZZWFyc0luQ3VycmVudFJvbGUsIGRhdGEgPSBpYm0gKQoKcGxvdChtb2RlbC5iZXN0LnRyYW5zZm9ybSAsIHdoaWNoID0gMSkKYm94Y294KG1vZGVsLmJlc3QudHJhbnNmb3JtLCBsYW1iZGEgPSBzZXEoMC4zLCAxLjIsIDAuMSkpCmBgYAojIyMjIFRyYW5zZm9ybWF0aW9uIG9mIG5vbiBmYWN0b3IgcHJlZGljdG9ycyBmb3IgYWxsIDMgbW9kZWxzIChhbGxyZWcsIHNpbXBsZSwgYmVzdCkKYGBge3J9CnRyYW5zZm9ybURvdWJsZXMgPSBmdW5jdGlvbihkZiwgZnVuYykgewogIGRibHMgPSBzYXBwbHkoZGYsIGlzLmRvdWJsZSkKICBjb3B5ID0gZGYKICBjb3B5WyxkYmxzXSA9IGZ1bmMoZGZbLGRibHNdKQogIGNvcHkKfQpgYGAKCgpgYGB7cn0KYWxscmVnTW9udGhseUluY29tZSA9IGlibSRNb250aGx5SW5jb21lXi43CmFsbHJlZy50cmFuc2Zvcm0ucHJlZGljdG9ycyA9IGlibVstYygxNyldCgojIyBXZSB0cmllZCAzIHR5cGVzIG9mIHRyYW5zZm9ybWF0aW9uIHRvIGFsbCBvZiBub24gZmFjdG9yIHByZWRpY3RvcnMgKGxuLCApCmFsbHJlZy50cmFuc2Zvcm0uZmFjdG9yID0gYWxscmVnLnRyYW5zZm9ybS5wcmVkaWN0b3JzWyxzYXBwbHkoYWxscmVnLnRyYW5zZm9ybS5wcmVkaWN0b3JzLCBpcy5mYWN0b3IpXQphbGxyZWcudHJhbnNmb3JtLmRvdWJsZSA9IGFsbHJlZy50cmFuc2Zvcm0ucHJlZGljdG9yc1ssc2FwcGx5KGFsbHJlZy50cmFuc2Zvcm0ucHJlZGljdG9ycywgaXMuZG91YmxlKV0KI2FsbHJlZy50cmFuc2Zvcm0uZG91YmxlID0gbGFwcGx5KGFsbHJlZy50cmFuc2Zvcm0uZG91YmxlLCBmdW5jdGlvbih4KSAxL3gpCiNhbGxyZWcudHJhbnNmb3JtLmRvdWJsZSA9IGV4cChhbGxyZWcudHJhbnNmb3JtLmRvdWJsZSArIDAuMDEpCmFsbHJlZy50cmFuc2Zvcm0uZG91YmxlID0gbG9nKGFsbHJlZy50cmFuc2Zvcm0uZG91YmxlICsgMC4wMSkKYWxscmVnLnRyYW5zZm9ybS5wcmVkaWN0b3JzID0gY2JpbmQoYWxscmVnLnRyYW5zZm9ybS5mYWN0b3IsIGFsbHJlZy50cmFuc2Zvcm0uZG91YmxlKQphbGxyZWcudHJhbnNmb3JtLnByZWRpY3RvcnMkTW9udGhseUluY29tZSA9IGFsbHJlZ01vbnRobHlJbmNvbWUKYGBgCmBgYHtyfQojIGNoZWNrIGZ1bmN0aW9uCnJlcyA9IHRyYW5zZm9ybURvdWJsZXMoYWxscmVnLnRyYW5zZm9ybS5wcmVkaWN0b3JzLCBmdW5jdGlvbih4KSB7IGxvZyh4ICsgMC4wMSkgfSkKcmVzJE1vbnRobHlJbmNvbWUgPSBpYm0kTW9udGhseUluY29tZV4uNyAjIHJldGFpbiBvcmlnaW5hbAojZGJscyA9IHNhcHBseShhbGxyZWcudHJhbnNmb3JtLnByZWRpY3RvcnMsIGlzLmRvdWJsZSkKI3Jlc1ssbmFtZXMoZGJsc1tkYmxzXSldID09IGFsbHJlZy50cmFuc2Zvcm0ucHJlZGljdG9yc1ssbmFtZXMoZGJsc1tkYmxzXSldCiNyZXNbLG5hbWVzKGFsbHJlZy50cmFuc2Zvcm0ucHJlZGljdG9ycyldID09IGFsbHJlZy50cmFuc2Zvcm0ucHJlZGljdG9yc1ssbmFtZXMoYWxscmVnLnRyYW5zZm9ybS5wcmVkaWN0b3JzKV0KYGBgCgoKYGBge3J9Cm1vZGVsLmFsbHJlZy50cmFuc2Zvcm0ucHJlZGljdG9ycyA9IGxtKE1vbnRobHlJbmNvbWUgfiAgSm9iTGV2ZWwgKyBKb2JSb2xlLCBkYXRhID0gYWxscmVnLnRyYW5zZm9ybS5wcmVkaWN0b3JzKQpwbG90KG1vZGVsLmFsbHJlZy50cmFuc2Zvcm0ucHJlZGljdG9ycyAsIHdoaWNoID0gMSkKYm94Y294KG1vZGVsLmFsbHJlZy50cmFuc2Zvcm0ucHJlZGljdG9ycywgbGFtYmRhID0gc2VxKDAuMywgMS4yLCAwLjEpKQpgYGAKCmBgYHtyfQpzaW1wbGVNb250aGx5SW5jb21lID0gaWJtJE1vbnRobHlJbmNvbWVeLjUKc2ltcGxlLnRyYW5zZm9ybS5wcmVkaWN0b3JzID0gaWJtWy1jKDE3KV0KCiMjIFdlIHRyaWVkIDMgdHlwZXMgb2YgdHJhbnNmb3JtYXRpb24gdG8gYWxsIG9mIG5vbiBmYWN0b3IgcHJlZGljdG9ycyAobG4sICkKc2ltcGxlLnRyYW5zZm9ybS5mYWN0b3IgPSBzaW1wbGUudHJhbnNmb3JtLnByZWRpY3RvcnNbLHNhcHBseShzaW1wbGUudHJhbnNmb3JtLnByZWRpY3RvcnMsIGlzLmZhY3RvcildCnNpbXBsZS50cmFuc2Zvcm0uZG91YmxlID0gc2ltcGxlLnRyYW5zZm9ybS5wcmVkaWN0b3JzWyxzYXBwbHkoc2ltcGxlLnRyYW5zZm9ybS5wcmVkaWN0b3JzLCBpcy5kb3VibGUpXQojc2ltcGxlLnRyYW5zZm9ybS5kb3VibGUgPSBsYXBwbHkoc2ltcGxlLnRyYW5zZm9ybS5kb3VibGUsIGZ1bmN0aW9uKHgpIDEveCkKI3NpbXBsZS50cmFuc2Zvcm0uZG91YmxlID0gZXhwKHNpbXBsZS50cmFuc2Zvcm0uZG91YmxlICsgMC4wMSkKc2ltcGxlLnRyYW5zZm9ybS5kb3VibGUgPSBsb2coc2ltcGxlLnRyYW5zZm9ybS5kb3VibGUgKyAwLjAxKQpzaW1wbGUudHJhbnNmb3JtLnByZWRpY3RvcnMgPSBjYmluZChzaW1wbGUudHJhbnNmb3JtLmZhY3Rvciwgc2ltcGxlLnRyYW5zZm9ybS5kb3VibGUpCnNpbXBsZS50cmFuc2Zvcm0ucHJlZGljdG9ycyRNb250aGx5SW5jb21lID0gc2ltcGxlTW9udGhseUluY29tZQoKbW9kZWwuc2ltcGxlLnRyYW5zZm9ybS5wcmVkaWN0b3JzID0gbG0oTW9udGhseUluY29tZSB+ICBKb2JMZXZlbCwgZGF0YSA9IHNpbXBsZS50cmFuc2Zvcm0ucHJlZGljdG9ycykKcGxvdChtb2RlbC5zaW1wbGUudHJhbnNmb3JtLnByZWRpY3RvcnMgLCB3aGljaCA9IDEpCmJveGNveChtb2RlbC5zaW1wbGUudHJhbnNmb3JtLnByZWRpY3RvcnMsIGxhbWJkYSA9IHNlcSgwLjMsIDEuMiwgMC4xKSkKYGBgCmBgYHtyfQpiZXN0TW9udGx5SW5jb21lID0gaWJtJE1vbnRobHlJbmNvbWVeLjcKYmVzdC50cmFuc2Zvcm0ucHJlZGljdG9ycyA9IGlibVstYygxNyldCgojIyBXZSB0cmllZCAzIHR5cGVzIG9mIHRyYW5zZm9ybWF0aW9uIHRvIGFsbCBvZiBub24gZmFjdG9yIHByZWRpY3RvcnMgCmJlc3QudHJhbnNmb3JtLmZhY3RvciA9IGJlc3QudHJhbnNmb3JtLnByZWRpY3RvcnNbLHNhcHBseShiZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzLCBpcy5mYWN0b3IpXQpiZXN0LnRyYW5zZm9ybS5kb3VibGUgPSBiZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzWyxzYXBwbHkoYmVzdC50cmFuc2Zvcm0ucHJlZGljdG9ycywgaXMuZG91YmxlKV0KI2Jlc3QudHJhbnNmb3JtLmRvdWJsZSA9IGxhcHBseShiZXN0LnRyYW5zZm9ybS5kb3VibGUsIGZ1bmN0aW9uKHgpIDEveCkKI2Jlc3QudHJhbnNmb3JtLmRvdWJsZSA9IGV4cChiZXN0LnRyYW5zZm9ybS5kb3VibGUgKyAwLjAxKQpiZXN0LnRyYW5zZm9ybS5kb3VibGUgPSBsb2coYmVzdC50cmFuc2Zvcm0uZG91YmxlICsgMC4wMSkKYmVzdC50cmFuc2Zvcm0ucHJlZGljdG9ycyA9IGNiaW5kKGJlc3QudHJhbnNmb3JtLmZhY3RvciwgYmVzdC50cmFuc2Zvcm0uZG91YmxlKQpiZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzJE1vbnRobHlJbmNvbWUgPSBiZXN0TW9udGx5SW5jb21lCgptb2RlbC5iZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzID0gbG0oTW9udGhseUluY29tZSB+IEdlbmRlciArIEpvYkludm9sdmVtZW50ICsgSm9iTGV2ZWwgKyAKICAgIEpvYlJvbGUgKyBOdW1Db21wYW5pZXNXb3JrZWQgKyBTdG9ja09wdGlvbkxldmVsICsgVG90YWxXb3JraW5nWWVhcnMgKyAKICAgIFllYXJzSW5DdXJyZW50Um9sZSwgZGF0YSA9IGJlc3QudHJhbnNmb3JtLnByZWRpY3RvcnMpCnBsb3QobW9kZWwuYmVzdC50cmFuc2Zvcm0ucHJlZGljdG9ycyAsIHdoaWNoID0gMSkKYm94Y294KG1vZGVsLmJlc3QudHJhbnNmb3JtLnByZWRpY3RvcnMsIGxhbWJkYSA9IHNlcSgwLjMsIDEuMiwgMC4xKSkKCiMgVHJhbnNmb3JtIFkgc2luY2UgYm94Y294IGRvZXMgbm90IGNvbnRhaW4gMQpiZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzJE1vbnRobHlJbmNvbWUgPSBiZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzJE1vbnRobHlJbmNvbWVeLjgKbW9kZWwuYmVzdC50cmFuc2Zvcm0ucHJlZGljdG9ycyA9IGxtKE1vbnRobHlJbmNvbWUgfiBHZW5kZXIgKyBKb2JJbnZvbHZlbWVudCArIEpvYkxldmVsICsgCiAgICBKb2JSb2xlICsgTnVtQ29tcGFuaWVzV29ya2VkICsgU3RvY2tPcHRpb25MZXZlbCArIFRvdGFsV29ya2luZ1llYXJzICsgCiAgICBZZWFyc0luQ3VycmVudFJvbGUsIGRhdGEgPSBiZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzKQpwbG90KG1vZGVsLmJlc3QudHJhbnNmb3JtLnByZWRpY3RvcnMgLCB3aGljaCA9IDEpCmJveGNveChtb2RlbC5iZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzLCBsYW1iZGEgPSBzZXEoMC4zLCAxLjIsIDAuMSkpCnN1bW1hcnkobW9kZWwuYmVzdC50cmFuc2Zvcm0ucHJlZGljdG9ycykKYGBgCiMjIyMgT3V0bGllcnMgQW5hbHlzaXMKYGBge3J9CiMjcmVzaWR1YWxzCnJlcyA9IG1vZGVsLmJlc3QudHJhbnNmb3JtLnByZWRpY3RvcnMkcmVzaWR1YWxzIAojI3N0dWRlbnRpemVkIHJlc2lkdWFscwpzdHVkZW50LnJlcyA9IHJzdGFuZGFyZChtb2RlbC5iZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzKSAKIyNleHRlcm5hbGx5IHN0dWRlbnRpemVkIHJlc2lkdWFscwpleHQuc3R1ZGVudC5yZXMgPSByc3R1ZGVudChtb2RlbC5iZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzKSAKCnBhcihtZnJvdyA9IGMoMSwzKSkKcGxvdChtb2RlbC5iZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzJGZpdHRlZC52YWx1ZXMscmVzLG1haW49IlJlc2lkdWFscyIpCnBsb3QobW9kZWwuYmVzdC50cmFuc2Zvcm0ucHJlZGljdG9ycyRmaXR0ZWQudmFsdWVzLHN0dWRlbnQucmVzLG1haW49IlN0dWRlbnRpemVkIFJlc2lkdWFscyIpCnBsb3QobW9kZWwuYmVzdC50cmFuc2Zvcm0ucHJlZGljdG9ycyRmaXR0ZWQudmFsdWVzLGV4dC5zdHVkZW50LnJlcyxtYWluPSJFeHRlcm5hbGx5ICBTdHVkZW50aXplZCBSZXNpZHVhbHMiKQoKbiA9IGxlbmd0aChiZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzJE1vbnRobHlJbmNvbWUpCnAgPSBsZW5ndGgoY29lZihtb2RlbC5iZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzKSkKCiMjY3JpdGljYWwgdmFsdWUgdXNpbmcgQm9uZmVycm9uaSBwcm9jZWR1cmUKZXh0LnN0dWRlbnQucmVzW2FicyhleHQuc3R1ZGVudC5yZXMpPnF0KDEtMC4wNS8oMipuKSwgbi1wLTEpXQoKYGBgCmBgYHtyfQojIExldmVyYWdlIHBvaW50CmxldjwtbG0uaW5mbHVlbmNlKG1vZGVsLmJlc3QudHJhbnNmb3JtLnByZWRpY3RvcnMpJGhhdCAKbGVuZ3RoKGxldltsZXY+MipwL25dKQoKYGBgCgpgYGB7cn0KaWJtW3doaWNoKGxldj4yKnAvbiksXQpgYGAKYGBge3J9CnBsb3QoaWJtJFBlcmZvcm1hbmNlUmF0aW5nKQpgYGAKCgpgYGB7cn0KREZGSVRTPC1kZmZpdHMobW9kZWwuYmVzdC50cmFuc2Zvcm0ucHJlZGljdG9ycykKREZGSVRTW2FicyhERkZJVFMpPjIqc3FydChwL24pXQpgYGAKYGBge3J9CkRGQkVUQVM8LWRmYmV0YXMobW9kZWwuYmVzdC50cmFuc2Zvcm0ucHJlZGljdG9ycykKbGVuZ3RoKERGQkVUQVNbYWJzKERGQkVUQVMpPjIvc3FydChuKV0pCiNsZW5ndGgoREZCRVRBUykKbnJvdyhpYm0pCmxlbmd0aChtb2RlbC5iZXN0LnRyYW5zZm9ybS5wcmVkaWN0b3JzJHJlc2lkdWFscykKYGBgCmBgYHtyfQpDT09LUzwtY29va3MuZGlzdGFuY2UobW9kZWwuYmVzdC50cmFuc2Zvcm0ucHJlZGljdG9ycykKQ09PS1NbQ09PS1M+cWYoMC41LHAsbi1wKV0KYGBgCgojIyMjIE1vZGVsIERpYWdub3N0aWNzCgpXZSBub3cgY2hlY2sgZm9yIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UuCgpgYGB7cn0KbGVuZ3RoKG1vZGVsLmJlc3QudHJhbnNmb3JtLnByZWRpY3RvcnMkY29lZmZpY2llbnRzKQpsZW5ndGgoaWJtWywxXSkKCmBgYApgYGB7cn0KbW9kZWwuc2ltcGxlLnJzdHVkZW50ICAgICA9IHJzdHVkZW50KG1vZGVsLnNpbXBsZSkgCm4gPSBucm93KGlibSkKcCA9IGxlbmd0aChjb2VmKG1vZGVsLnNpbXBsZSkpCnBsb3QobW9kZWwuc2ltcGxlLnJzdHVkZW50LG1haW49IkV4dGVybmFsbHkgU3R1ZGVudGl6ZWQgUmVzaWR1YWxzIiwgeWxpbT1jKC00LDQpKQphYmxpbmUoaD1xdCgxLTAuMDUvKDIqbiksIG4tcC0xKSwgY29sPSJyZWQiKQphYmxpbmUoaD0tcXQoMS0wLjA1LygyKm4pLCBuLXAtMSksIGNvbD0icmVkIikKbW9kZWwuc2ltcGxlLnJzdHVkZW50W2Ficyhtb2RlbC5zaW1wbGUucnN0dWRlbnQpPnF0KDEtMC4wNS8oMipuKSwgbi1wLTEpXQoKYGBgCgpgYGB7cn0KbGV2ID0gbG0uaW5mbHVlbmNlKG1vZGVsLnNpbXBsZSkkaGF0IAppbmZsdWVuY2UgPSBsZXZbbGV2PjIqcC9uXQp7CiAgcGxvdChsZXYsIG1haW49IkxldmVyYWdlcyIsIHlsaW09YygtMC44LDAuOCkpCiAgYWJsaW5lKGg9MipwL24sIGNvbD0icmVkIikKICBpZGVudGlmeShsZXYpCn0KCmlibVt3aGljaChsZXY+MipwL24pLF0KYGBgCgpgYGB7cn0KaWJtLm5vc2ltcGxlbGV2ID0gaWJtWy13aGljaChsZXY+MipwL24pLF0KYGBgCgpgYGB7cn0KbW9kZWwuc2ltcGxlLm5vbGV2ID0gbG0oTW9udGhseUluY29tZSB+IEpvYkxldmVsLCBkYXRhID0gaWJtLm5vc2ltcGxlbGV2KQpwbG90KG1vZGVsLnNpbXBsZS5ub2xldikKYGBgCgpgYGB7cn0KcGxvdChtb2RlbC5zaW1wbGUpCmBgYAoKYGBge3J9CiMgQ3JlYXRlIHN1YnNldHMgZm9yIGVhY2ggam9iIGxldmVsCmpvYkxldmVsMSA9IHN1YnNldChpYm0sIEpvYkxldmVsID09IDEpCmpvYkxldmVsMiA9IHN1YnNldChpYm0sIEpvYkxldmVsID09IDIpCmpvYkxldmVsMyA9IHN1YnNldChpYm0sIEpvYkxldmVsID09IDMpCmpvYkxldmVsNCA9IHN1YnNldChpYm0sIEpvYkxldmVsID09IDQpCmpvYkxldmVsNSA9IHN1YnNldChpYm0sIEpvYkxldmVsID09IDUpCmBgYAoKYGBge3J9CiMgdXNpbmcgZXhoYXVzdGl2ZSBzZWFyY2ggZm9yIGpvYiBsZXZlbCAxCmFsbHJlZy5qb2JMZXZlbDEgPC0gcmVnc3Vic2V0cyhNb250aGx5SW5jb21lIH4uLCBkYXRhPWpvYkxldmVsMSwgbmJlc3Q9MSwgcmVhbGx5LmJpZyA9IFQpCmBgYAoKYGBge3J9CiMgc2hvd2luZyB0aGUgb3V0Y29tZSBvZiBleGhhdXN0aXZlIHNlYXJjaCBmb3Igam9iIGxldmVsIDEKcHBfYWxscmVnKGFsbHJlZy5qb2JMZXZlbDEpCmBgYAoKYGBge3J9CiMgZml0IHJlZ3Jlc3Npb24gbW9kZWwgZm9yIGJlc3QgcjIgbW9kZWwgZm9yIGpvYiBsZXZlbCAxCm1vZGVsLmpvYkxldmVsMSA9IGxtKE1vbnRobHlJbmNvbWUgfiAxICsgQXR0cml0aW9uICsgSm9iUm9sZSArIE51bUNvbXBhbmllc1dvcmtlZCArIFBlcmNlbnRTYWxhcnlIaWtlICsgUGVyZm9ybWFuY2VSYXRpbmcgKyBUb3RhbFdvcmtpbmdZZWFycyArIFRyYWluaW5nVGltZXNMYXN0WWVhciArIFllYXJzSW5DdXJyZW50Um9sZSArIFllYXJzU2luY2VMYXN0UHJvbW90aW9uLCBkYXRhPWpvYkxldmVsMSkKCnN1bW1hcnkobW9kZWwuam9iTGV2ZWwxKQpwbG90KG1vZGVsLmpvYkxldmVsMSwgd2hpY2g9MToyKQpgYGAKCmBgYHtyfQojIHVzaW5nIGV4aGF1c3RpdmUgc2VhcmNoIGZvciBqb2IgbGV2ZWwgMgphbGxyZWcuam9iTGV2ZWwyIDwtIHJlZ3N1YnNldHMoTW9udGhseUluY29tZSB+LiwgZGF0YT1qb2JMZXZlbDIsIG5iZXN0PTEsIHJlYWxseS5iaWcgPSBUKQpgYGAKCmBgYHtyfQojIHNob3dpbmcgdGhlIG91dGNvbWUgb2YgZXhoYXVzdGl2ZSBzZWFyY2ggZm9yIGpvYiBsZXZlbCAyCnBwX2FsbHJlZyhhbGxyZWcuam9iTGV2ZWwyKQpgYGAKCmBgYHtyfQojIGZpdCByZWdyZXNzaW9uIG1vZGVsIGZvciBiZXN0IHIyIG1vZGVsIGZvciBqb2IgbGV2ZWwgMgptb2RlbC5qb2JMZXZlbDIgPSBsbShNb250aGx5SW5jb21lIH4gMSArIEJ1c2luZXNzVHJhdmVsICsgRGFpbHlSYXRlICsgRWR1Y2F0aW9uICsgRWR1Y2F0aW9uRmllbGQgKyBKb2JJbnZvbHZlbWVudCArIEpvYlJvbGUgKyBUb3RhbFdvcmtpbmdZZWFycywgZGF0YT1qb2JMZXZlbDIpCgpzdW1tYXJ5KG1vZGVsLmpvYkxldmVsMikKcGxvdChtb2RlbC5qb2JMZXZlbDIsIHdoaWNoPTE6MikKYGBgCgpgYGB7cn0KIyB1c2luZyBleGhhdXN0aXZlIHNlYXJjaCBmb3Igam9iIGxldmVsIDMKYWxscmVnLmpvYkxldmVsMyA8LSByZWdzdWJzZXRzKE1vbnRobHlJbmNvbWUgfi4sIGRhdGE9am9iTGV2ZWwzLCBuYmVzdD0xLCByZWFsbHkuYmlnID0gVCkKYGBgCgpgYGB7cn0KIyBzaG93aW5nIHRoZSBvdXRjb21lIG9mIGV4aGF1c3RpdmUgc2VhcmNoIGZvciBqb2IgbGV2ZWwgMwpwcF9hbGxyZWcoYWxscmVnLmpvYkxldmVsMykKYGBgCgpgYGB7cn0KIyBmaXQgcmVncmVzc2lvbiBtb2RlbCBmb3IgYmVzdCByMiBtb2RlbCBmb3Igam9iIGxldmVsIDMKbW9kZWwuam9iTGV2ZWwzID0gbG0oTW9udGhseUluY29tZSB+IDEgKyBCdXNpbmVzc1RyYXZlbCArIEVkdWNhdGlvbiArIEpvYlJvbGUgKyBOdW1Db21wYW5pZXNXb3JrZWQgKyBSZWxhdGlvbnNoaXBTYXRpc2ZhY3Rpb24gKyBTdG9ja09wdGlvbkxldmVsICsgVG90YWxXb3JraW5nWWVhcnMsIGRhdGE9am9iTGV2ZWwzKQoKc3VtbWFyeShtb2RlbC5qb2JMZXZlbDMpCnBsb3QobW9kZWwuam9iTGV2ZWwzLCB3aGljaD0xOjIpCmBgYAoKCmBgYHtyfQojIHVzaW5nIGV4aGF1c3RpdmUgc2VhcmNoIGZvciBqb2IgbGV2ZWwgNAphbGxyZWcuam9iTGV2ZWw0IDwtIHJlZ3N1YnNldHMoTW9udGhseUluY29tZSB+LiwgZGF0YT1qb2JMZXZlbDQsIG5iZXN0PTEsIHJlYWxseS5iaWcgPSBUKQpgYGAKCmBgYHtyfQojIHNob3dpbmcgdGhlIG91dGNvbWUgb2YgZXhoYXVzdGl2ZSBzZWFyY2ggZm9yIGpvYiBsZXZlbCA0CnBwX2FsbHJlZyhhbGxyZWcuam9iTGV2ZWw0KQpgYGAKCmBgYHtyfQojIGZpdCByZWdyZXNzaW9uIG1vZGVsIGZvciBiZXN0IHIyIG1vZGVsIGZvciBqb2IgbGV2ZWwgNAptb2RlbC5qb2JMZXZlbDQgPSBsbShNb250aGx5SW5jb21lIH4gMSArIEVkdWNhdGlvbiArIEVudmlyb25tZW50U2F0aXNmYWN0aW9uICsgSm9iUm9sZSArIEpvYlNhdGlzZmFjdGlvbiArIE1hcml0YWxTdGF0dXMgKyBNb250aGx5UmF0ZSArIFBlcmZvcm1hbmNlUmF0aW5nICsgUmVsYXRpb25zaGlwU2F0aXNmYWN0aW9uLCBkYXRhPWpvYkxldmVsNCkKCnN1bW1hcnkobW9kZWwuam9iTGV2ZWw0KQpwbG90KG1vZGVsLmpvYkxldmVsNCwgd2hpY2g9MToyKQpgYGAKCgpgYGB7cn0KIyB1c2luZyBleGhhdXN0aXZlIHNlYXJjaCBmb3Igam9iIGxldmVsIDUKYWxscmVnLmpvYkxldmVsNSA8LSByZWdzdWJzZXRzKE1vbnRobHlJbmNvbWUgfi4sIGRhdGE9am9iTGV2ZWw1LCBuYmVzdD0xLCByZWFsbHkuYmlnID0gVCkKYGBgCgpgYGB7cn0KIyBzaG93aW5nIHRoZSBvdXRjb21lIG9mIGV4aGF1c3RpdmUgc2VhcmNoIGZvciBqb2IgbGV2ZWwgNQpwcF9hbGxyZWcoYWxscmVnLmpvYkxldmVsNSkKYGBgCmBgYHtyfQojIGZpdCByZWdyZXNzaW9uIG1vZGVsIGZvciBiZXN0IHIyIG1vZGVsIGZvciBqb2IgbGV2ZWwgNQptb2RlbC5qb2JMZXZlbDUgPSBsbShNb250aGx5SW5jb21lIH4gMSArIEJ1c2luZXNzVHJhdmVsICsgRGFpbHlSYXRlICsgRWR1Y2F0aW9uICsgRWR1Y2F0aW9uRmllbGQgKyBFZHVjYXRpb25GaWVsZCArIFBlcmNlbnRTYWxhcnlIaWtlICsgUmVsYXRpb25zaGlwU2F0aXNmYWN0aW9uICsgU3RvY2tPcHRpb25MZXZlbCwgZGF0YT1qb2JMZXZlbDUpCgpzdW1tYXJ5KG1vZGVsLmpvYkxldmVsNSkKcGxvdChtb2RlbC5qb2JMZXZlbDUsIHdoaWNoPTE6MikKYGBgCg==